From ae21a0ae661ea1056b026169278deb9b742517cf Mon Sep 17 00:00:00 2001 From: Timmy Date: Mon, 9 Mar 2026 13:51:45 +0800 Subject: [PATCH] Initial commit: Lunar calendar converter project - Add lunar calendar astronomical calculation engine (lunar_calendar.py) - Add converter utilities for solar/lunar date conversion - Add historical era name conversions (chrono_converter.py, converter_with_eras.py) - Add FastAPI REST API for date conversion services - Add age calculator using lunar calendar - Add batch generation script for lunar calendar JSON data - Add project documentation (CLAUDE.md, pyproject.toml) Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 26 ++ .tarignore | 7 + .vscode/extensions.json | 10 + .vscode/settings.json | 94 ++++++ .vscode/sftp.example.jsonc | 57 ++++ CLAUDE.md | 112 +++++++ Lunar_Calendar_Converter_README | 123 ++++++++ age_calculator.py | 99 ++++++ api.py | 110 +++++++ chrono_converter.py | 213 +++++++++++++ converter.py | 107 +++++++ converter_with_eras.py | 174 +++++++++++ generate_lunar_calendar_json.py | 29 ++ lunar_calendar.py | 225 ++++++++++++++ pyproject.toml | 16 + pyrightconfig.json | 14 + ruff.toml | 33 ++ run_uvicorn.sh | 4 + uv.lock | 524 ++++++++++++++++++++++++++++++++ 19 files changed, 1977 insertions(+) create mode 100644 .gitignore create mode 100644 .tarignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/sftp.example.jsonc create mode 100644 CLAUDE.md create mode 100644 Lunar_Calendar_Converter_README create mode 100644 age_calculator.py create mode 100644 api.py create mode 100644 chrono_converter.py create mode 100644 converter.py create mode 100644 converter_with_eras.py create mode 100644 generate_lunar_calendar_json.py create mode 100644 lunar_calendar.py create mode 100644 pyproject.toml create mode 100644 pyrightconfig.json create mode 100644 ruff.toml create mode 100644 run_uvicorn.sh create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f89404 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Local editor/credentials files +.vscode/sftp.json + +# Python caches +__pycache__/ +*.pyc +.pytest_cache/ +.ruff_cache/ +.mypy_cache/ + +# Build outputs +build/ +dist/ + +# Virtual environment +.venv/ +venv/ + +# Generated lunar calendar data +lunar_json/ + +# JPL ephemeris files (large binary files, download separately) +*.bsp + +# tar archives +*.tar.gz diff --git a/.tarignore b/.tarignore new file mode 100644 index 0000000..494ebd2 --- /dev/null +++ b/.tarignore @@ -0,0 +1,7 @@ +__pycache__ +.pytest_cache +.git +.venv +.DS_Store + +uv.lock diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..434b462 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // 推薦安裝的擴充(可於側邊欄 Extensions 接受建議安裝) + "recommendations": [ + "charliermarsh.ruff", + "ms-python.python", + "ms-python.vscode-pylance", + "ms-toolsai.jupyter", + "Natizyskunk.sftp" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4903f50 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,94 @@ +{ + // ——— 工作區 VS Code 設定 ——— + // 需安裝擴充(建議): + // 1) charliermarsh.ruff → Python 格式化 + 匯入排序 + Lint/修復 + // 2) ms-python.python → Python 語言支援、測試、偵錯、終端環境 + // 3) ms-python.vscode-pylance → 型別分析/智慧提示(使用 Pylance) + // 4) ms-toolsai.jupyter →(可選)Notebook 支援 + // 也可直接接受工作區推薦(.vscode/extensions.json) + + // 檔案總管把雜物藏起來,乾淨一點 + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/__pycache__": true, + "**/.DS_Store": true + }, + + // 不愛被一直彈參數提示,關掉就清爽 + "editor.parameterHints.enabled": false, + + // 字型&連字:看起來順眼一點 + "editor.fontFamily": "JetBrainsMono Nerd Font Mono", + "editor.fontLigatures": true, + + // 介面縮放:0 就是原尺寸,自己再調 + "window.zoomLevel": 0, + + // 存檔只格式化實際修改的區塊(減少整檔 diff) + "editor.formatOnSaveMode": "modificationsIfAvailable", + + // 若未安裝 Code Runner,避免未知設定警告:先移除相關設定 + + // 版面微調:全域用空白縮排,預設 2 格;換行一律 LF + "editor.insertSpaces": true, + "editor.tabSize": 2, + "files.eol": "\n", + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + // 專案行寬 100 欄(與 Ruff 設定一致) + "editor.rulers": [100], + + // 使用 Ruff:Lint + 匯入排序 + 格式化(見 ruff.toml) + + // Python 語言專屬設定 + "[python]": { + // 以 Ruff 當預設格式器 + "editor.defaultFormatter": "charliermarsh.ruff", + // 存檔:格式化 + 自動修復 + 匯入排序 + "editor.formatOnSave": true, + // 交給 Ruff 做自動修復與匯入排序(避免與 isort 重疊) + "editor.codeActionsOnSave": { + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" + }, + // Python 習慣 4 空白縮排 + "editor.tabSize": 4 + }, + + // Pylance/分析:全專案診斷(型別等級改由 pyrightconfig.json 管理) + "python.analysis.diagnosticMode": "workspace", + "python.terminal.activateEnvironment": true, + // 指定預設的工作區 Interpreter(本機虛擬環境) + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python", + // 若需調整 Pylance 型別檢查或診斷嚴重度,請至 pyrightconfig.json + + // 測試(若使用 pytest):VS Code 直接偵測測試並執行 + "python.testing.pytestEnabled": true, + "python.testing.unittestEnabled": false, + "python.testing.pytestArgs": ["tests"], + + // 搜尋/監控忽略更多常見產物與虛擬環境 + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "**/.venv": true, + "**/venv": true, + "**/.tox": true, + "**/.mypy_cache": true, + "**/.pytest_cache": true, + "**/.ruff_cache": true, + "**/dist": true, + "**/build": true + }, + "search.useIgnoreFiles": true, + "files.watcherExclude": { + "**/node_modules/**": true, + "**/.venv/**": true, + "**/venv/**": true, + "**/.tox/**": true, + "**/.git/**": true + } +} diff --git a/.vscode/sftp.example.jsonc b/.vscode/sftp.example.jsonc new file mode 100644 index 0000000..101cb7a --- /dev/null +++ b/.vscode/sftp.example.jsonc @@ -0,0 +1,57 @@ +{ + // 提示:將本檔複製為 .vscode/sftp.json(純 JSON,勿含註解)後再編輯。 + + // 顯示用名稱(任意字串) + "name": "My Server", + + // 伺服器位址(IP 或 ~/.ssh/config 的 Host 別名) + "host": "192.168.42.108", + + // 傳輸協定:sftp(SSH 上的檔案傳輸) + "protocol": "sftp", + + // SSH 連線埠,預設 22 + "port": 22, + + // 登入使用者(例:root) + "username": "root", + + // ——— 擇一方式(請只保留其中一種)——— + // A) 使用系統環境中的 ssh-agent(SSH_AUTH_SOCK) + // "agent": "env", + // B) 直接指定私鑰路徑(例如 ECDSA) + "privateKeyPath": "~/.ssh/id_ecdsa", + // 若金鑰有通關詞,請移除此欄並讓 agent 代為解鎖 + // "passphrase": "不建議填在設定檔", + // 參考本機 SSH 設定(可選) + "sshConfigPath": "~/.ssh/config", + // 不回退互動式密碼驗證 + "interactiveAuth": false, + + // ——— 可選:改用 OpenSSH 設定 —— + // 若想完全沿用 ~/.ssh/config 與 multiplex,可改為: + // "openSsh": true, 並刪除上方 agent/privateKeyPath 等欄位 + + // 遠端根目錄(所有上下載相對於此路徑) + "remotePath": "/opt/lunar-converter", + + // 存檔即自動上傳:true 開啟;若不想每次存檔都上傳,改為 false + "uploadOnSave": true, + + // 上傳時先用暫存檔再改名:false 直接覆寫;若遠端需要原子寫入可設 true + "useTempFile": false, + + // 忽略不需上傳的檔案/資料夾 + "ignore": [ + "**/.git/**", + "**/.DS_Store", + "**/node_modules/**", + "**/.venv/**", + "**/venv/**", + "**/.mypy_cache/**", + "**/.pytest_cache/**", + "**/.ruff_cache/**", + "**/dist/**", + "**/build/**" + ] +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ea10a94 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,112 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a lunar calendar converter (lunar-converter) that provides: +- Western (Gregorian) calendar ↔ Chinese lunar calendar conversion +- Historical era name conversions (Ming/Qing dynasties, Taiping Heavenly Kingdom, Japanese eras, Republic of China era) +- Age calculation (real age and East Asian nominal age) +- REST API for date conversion services + +## Core Architecture + +### Data Generation Pipeline + +1. **`lunar_calendar.py`** - Astronomical calculation engine using Skyfield + - Computes lunar calendar data from first principles using JPL ephemeris + - Uses `de421.bsp` / `de422.bsp` / `de440s.bsp` ephemeris files (must exist in working directory) + - Calculates new moons, solar terms (24 jieqi), and determines leap months + - Generates ganzhi (sexagenary cycle) and zodiac animals + - Output: yearly JSON files with complete lunar calendar mapping + +2. **`generate_lunar_calendar_json.py`** - Batch generation script + - Generates JSON files for multiple years in parallel using multiprocessing + - Outputs to `lunar_json/{year}.json` directory + - Usage: `python generate_lunar_calendar_json.py` + +### Conversion Layer + +3. **`converter.py`** - Primary conversion class using JSON data + - `Converter.solar_to_lunar(date_str)` - Convert "YYYY-MM-DD" to lunar date info + - `Converter.lunar_to_solar(year, month, day, leap=False)` - Reverse conversion + - `Converter.get_leap_month(year)` - Return leap month number or None + - Depends on `lunar_json/` directory containing pre-generated JSON files + +### Historical Era Conversion + +4. **`chrono_converter.py`** - Historical era name conversions + - Converts Western dates to era names (Ming/Qing dynasties, Taiping, Minguo) + - Contains era date ranges as class constants + +5. **`converter_with_eras.py`** - Combined converter (CLI tool) + - Merges lunar conversion with era name conversion + - CLI usage: `python converter_with_eras.py solar2lunar YYYY-MM-DD` + - CLI usage: `python converter_with_eras.py lunar2solar YYYY-MM-DD [--leap]` + - CLI usage: `python converter_with_eras.py era YYYY-MM-DD` + +### Applications + +6. **`api.py`** - FastAPI REST API server + - `GET /solar2lunar?date=YYYY-MM-DD` - Solar to lunar conversion + - `GET /lunar2solar?date=YYYY-MM-DD` - Lunar to solar (handles leap months) + - `GET /leap-info?year=YYYY` - Leap month info for a year + - Runs on a dynamically-assigned free port + +7. **`age_calculator.py`** - Age calculator using lunar calendar + - Calculates "real age" (足歲) and "East Asian age" (虛歲) + - Uses dependency injection pattern with `Converter` + - Accurately handles leap month birthdays + +## Common Commands + +### Generate lunar calendar data (required before conversions work) +```bash +# Generate all years (1-2149, parallel processing) +python generate_lunar_calendar_json.py + +# Generate single year +python lunar_calendar.py 2025 +``` + +### Run conversions +```bash +# Solar to lunar +python -c "from converter import Converter; print(Converter().solar_to_lunar('2025-01-01'))" + +# Lunar to solar +python -c "from converter import Converter; print(Converter().lunar_to_solar(2025, 1, 1))" + +# Era conversion +python converter_with_eras.py era 1900-01-01 +``` + +### Run API server +```bash +python api.py +``` + +### Run tests +```bash +pytest +``` + +## Important Notes + +- **Ephemeris files**: The JPL ephemeris files (`de421.bsp`, `de422.bsp`, or `de440s.bsp`) are required for astronomical calculations. These must be present in the working directory. `lunar_calendar.py:92` selects which ephemeris to use. + +- **Data dependency**: `converter.py` and dependent modules require pre-generated JSON files in `lunar_json/` directory. Generate these first using `generate_lunar_calendar_json.py` or `lunar_calendar.py`. + +- **Calendar systems**: The code handles multiple calendar systems: + - Gregorian (Western) calendar + - Chinese lunar calendar (with leap months) + - Historical era names (Chinese dynasties, Japanese eras) + - Republic of China (Minguo) calendar + +- **Timezone**: All calculations use UTC+8 (Taipei timezone) as defined in `LunarCalendar.TZ_OFFSET`. + +- **Date ranges**: + - Chinese eras defined in `converter_with_eras.py:15-31` (CHINESE_ERAS_SOLAR) + - Japanese eras defined in `converter_with_eras.py:91-98` (JAPANESE_ERAS) + - Taiping Heavenly Kingdom: 1851-02-01 to 1872-07-05 diff --git a/Lunar_Calendar_Converter_README b/Lunar_Calendar_Converter_README new file mode 100644 index 0000000..891df55 --- /dev/null +++ b/Lunar_Calendar_Converter_README @@ -0,0 +1,123 @@ +Lunar Calendar Converter 農曆轉換工具 + +一套使用 Python 3.13+ 與 Skyfield 天文資料庫實作的農曆轉換工具,支援西曆↔農曆、閏月查詢,並提供 CLI 與 API(FastAPI)。 + +需求 + +- Python >= 3.13(可用 uv 安裝) +- 套件: + - skyfield + - jplephem + - numpy + - fastapi、uvicorn(僅在使用 API 時) +- Skyfield 天文資料檔(本庫已附): + - de422.bsp(程式目前預設使用) + - de440s.bsp、de421.bsp(可替代) + +安裝與環境(使用 uv) + +初始化與安裝依賴(如已存在可略過 init) +uv init lunar-converter +cd lunar-converter +uv add skyfield jplephem numpy flask +uv lock +uv sync + +uv 會自動建立 .venv/ 虛擬環境,無需手動啟動。 + +專案結構(重點檔案) + +. +├── converter.py # 西曆↔農曆轉換核心與閏月查詢(供程式/API 使用) +├── converter_with_eras.py # 指令列工具:solar2lunar / lunar2solar / era +├── api.py # FastAPI 服務 +├── generate_lunar_calendar_json.py # 批次產生 JSON(可自訂年份範圍) +├── lunar_calendar.py # 天文計算與 JSON 產生 +├── lunar_json/ # 每年一檔的農曆 JSON(執行產生器後) + +產生農曆資料(JSON) + +預設腳本為 `generate_lunar_calendar_json.py`,內含可調整的年份範圍。 + +uv run python generate_lunar_calendar_json.py + +會輸出到 `lunar_json/`,例如:`lunar_json/2025.json`。 + +若只想產生 1900–2100,可修改腳本內的 years 範圍: + +# generate_lunar_calendar_json.py +# years = range(1900, 2101) + +指令列(CLI)使用 + +CLI 功能位於 `converter_with_eras.py`: + +uv run python converter_with_eras.py solar2lunar 2025-10-12 +uv run python converter_with_eras.py lunar2solar 1977-08-30 --leap # 閏月請加上 --leap +uv run python converter_with_eras.py era 1906-02-07 # 歷代年號 / 日本元號查詢 + +範例輸出(solar2lunar) + +{ + "西曆": "2025-10-12", + "農曆": "8月21日", + "干支": "乙巳", + "生肖": "蛇", + "節氣": "" +} + +啟用 API(FastAPI) + +uv run python api.py + +程式會自動尋找可用埠(預設從 5000 開始)。可瀏覽 `/docs` 或 `/redoc` 查看互動式文件。 +或直接用 Uvicorn 啟動: + +uv run uvicorn api:app --host 0.0.0.0 --port 5000 + +API 範例 + +# 查詢一個存在的日期 +curl "http://127.0.0.1:5001/solar2lunar?date=2025-10-06" | jq + +# 不提供 date 參數 +curl "http://127.0.0.1:5001/solar2lunar" | jq + +# 查詢一個沒有閏月的日期 +curl "http://127.0.0.1:5001/lunar2solar?date=1977-08-30" | jq + +# 查詢一個有閏月的日期 +curl "http://127.0.0.1:5001/lunar2solar?date=1976-08-25" | jq + +# 查詢一個不存在的日期(會得到 404) +curl -i "http://127.0.0.1:5001/lunar2solar?date=2025-08-30" + +# 查詢 2023 年(該年有閏二月) +curl "http://127.0.0.1:5001/leap-info?year=2023" | jq + +# 查詢 1977 年 +curl "http://127.0.0.1:5001/leap-info?year=1977" | jq + +# 提供一個非數字的年份 +curl "http://127.0.0.1:5001/leap-info?year=abc" | jq + +備註 + +- 干支與生肖依據「農曆年」切換。 +- 使用 Skyfield 計算節氣與朔望月,精度高於傳統對照表。 +- 目前 `lunar_calendar.py` 預設載入 `de422.bsp`。欲改用 `de440s.bsp` 或 `de421.bsp`,請修改: + self.eph = load('de422.bsp') +- 若缺少某年 JSON 檔,查詢會提示錯誤,請先執行產生器產生對應年份。 + +作者 + +由 Timmy(羅楊竣)開發。 + +MIT License + +開發小工具(直接使用 uv 指令) + +- `uv run --group dev black .`:使用 Black 格式化專案 +- `uv run --group dev ruff format .`:使用 Ruff 內建 formatter 格式化 +- `uv run --group dev ruff check .`:Ruff 檢查 +- `uv run --group dev ruff check --fix .`:Ruff 自動修正 diff --git a/age_calculator.py b/age_calculator.py new file mode 100644 index 0000000..c88c95e --- /dev/null +++ b/age_calculator.py @@ -0,0 +1,99 @@ +# age_calculator.py +import datetime +from collections import namedtuple +from loguru import logger # type: ignore[reportMissingImports] + +# 從 converter.py 這個檔案,匯入 Converter 這個 class +from converter import Converter + +# --- 類型定義 --- +AgeResult = namedtuple("AgeResult", ["real_age", "east_asian_age"]) + +# ====================================================================== +# 主要類別:AgeCalculator +# ====================================================================== +class AgeCalculator: + """ + 提供精準的年齡計算功能 (實歲與虛歲)。 + 初始化時需要傳入一個 Converter 物件。 + """ + + def __init__(self, converter: Converter): + """ + 初始化年齡計算機。 + 這個設計模式稱為「依賴注入 (Dependency Injection)」, + AgeCalculator 本身不建立 Converter,而是由外部提供,讓架構更靈活。 + + :param converter: 一個 Converter 的實體物件。 + """ + if not isinstance(converter, Converter): + raise TypeError("初始化 AgeCalculator 時,必須提供一個有效的 Converter 物件") + self.converter = converter + + def calculate( + self, + solar_birth_date: datetime.date, + lunar_birth_info: dict, + today: datetime.date + ) -> AgeResult: + """ + 根據生日資訊,使用農曆轉換器精準計算實歲和虛歲。 + """ + # 虛歲計算 (邏輯不變) + east_asian_age = today.year - solar_birth_date.year + 1 + + # 實歲計算 (使用 self.converter) + try: + # 用 converter 找出「今年的農曆生日」對應的西曆日期 + this_year_lunar_bday_solar_str = self.converter.lunar_to_solar( + lunar_year=today.year, + lunar_month=lunar_birth_info['month'], + lunar_day=lunar_birth_info['day'], + leap=lunar_birth_info['leap'] + ) + this_year_lunar_bday_date = datetime.datetime.strptime(this_year_lunar_bday_solar_str, '%Y-%m-%d').date() + except KeyError: + # Edge case: 若今年沒有該農曆日期(如閏月),則認定生日已過,讓邏輯更強健 + this_year_lunar_bday_date = today - datetime.timedelta(days=1) + + real_age = today.year - solar_birth_date.year + # 核心判斷:如果今天還沒到「今年的農曆生日(的西曆日期)」,那實歲就 -1 + if today < this_year_lunar_bday_date: + real_age -= 1 + + return AgeResult(real_age=real_age, east_asian_age=east_asian_age) + + +# ====================================================================== +# 主程式區塊:作為使用範例 +# ====================================================================== +if __name__ == "__main__": + # --- 作為範例的資料 --- + TODAY_OVERRIDE = datetime.date(2025, 7, 30) + SOLAR_BIRTH_DATE_STR = "1977-10-12" + LUNAR_BIRTH_INFO = {"month": 8, "day": 30, "leap": False} + + try: + # --- 步驟 1: 建立依賴物件 (工具人) --- + converter = Converter() + + # --- 步驟 2: 建立主物件,並注入依賴 (主角登場,並把工具人交給他) --- + age_calculator = AgeCalculator(converter=converter) + + # --- 步驟 3: 執行計算 (叫主角開始幹活) --- + solar_birth_date = datetime.datetime.strptime(SOLAR_BIRTH_DATE_STR, "%Y-%m-%d").date() + age_result = age_calculator.calculate( + solar_birth_date=solar_birth_date, + lunar_birth_info=LUNAR_BIRTH_INFO, + today=TODAY_OVERRIDE, + ) + + # --- 步驟 4: 使用 loguru 輸出結果 --- + logger.remove() + logger.add(lambda msg: print(msg, end=""), format="{message}") + + logger.info(f"實歲 (足歲):{age_result.real_age} 歲。") + logger.info(f"虛歲:{age_result.east_asian_age} 歲。") + + except Exception as e: + logger.error(f"執行時發生錯誤: {e}") diff --git a/api.py b/api.py new file mode 100644 index 0000000..5727397 --- /dev/null +++ b/api.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# 提供農曆-西曆轉換的 API 服務(FastAPI 版本) +# 依賴: fastapi, uvicorn, converter.py + +# /// script +# requires-python = ">=3.12" +# dependencies = [ +# "fastapi", +# "uvicorn", +# ] +# /// + +import socket +from contextlib import closing +from typing import Any, Dict, List + +from fastapi import FastAPI, Query # type: ignore[reportMissingImports] +from fastapi.responses import JSONResponse # type: ignore[reportMissingImports] +import uvicorn # type: ignore[reportMissingImports] + +from converter import Converter + + +def find_free_port() -> int: + """使用臨時 socket 綁定到 0 取得可用埠號""" + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s: + s.bind(("", 0)) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + return s.getsockname()[1] + + +app = FastAPI(title="Lunar Calendar Converter API") +conv = Converter() + + +@app.get("/solar2lunar") +def solar2lunar_endpoint(date: str = Query(None, description="YYYY-MM-DD")): + """西曆轉農曆""" + if not date: + return JSONResponse({"error": "缺少參數 date,格式 YYYY-MM-DD"}, status_code=400) + try: + info_raw = conv.solar_to_lunar(date) + info = { + "gregorian": info_raw.get("西曆"), + "lunar": info_raw.get("農曆"), + "ganzhi": info_raw.get("干支"), + "zodiac": info_raw.get("生肖"), + "solar_term": info_raw.get("節氣"), + } + return info + except Exception as e: + return JSONResponse({"error": str(e)}, status_code=400) + + +@app.get("/lunar2solar") +def lunar2solar_endpoint(date: str = Query(None, description="YYYY-MM-DD")): + """農曆轉西曆(自動處理閏月,回傳所有可能性)""" + if not date: + return JSONResponse({"error": "缺少參數 date,格式 YYYY-MM-DD"}, status_code=400) + + try: + y_str, m_str, d_str = date.split("-", 2) + y, m, d = int(y_str), int(m_str), int(d_str) + except Exception: + return JSONResponse({"error": "農曆日期格式應為 YYYY-MM-DD"}, status_code=400) + + possible_solar_dates: List[str] = conv.lunar_to_solar_all(y, m, d) + if not possible_solar_dates: + error_msg = f"找不到任何對應農曆 {y}年{m}月{d}日 的西曆日期" + return JSONResponse({"error": error_msg}, status_code=404) + + results_list: List[Dict[str, Any]] = [] + for solar_date in possible_solar_dates: + try: + info_raw = conv.solar_to_lunar(solar_date) + results_list.append({ + "gregorian": info_raw.get("西曆"), + "lunar": info_raw.get("農曆"), + "ganzhi": info_raw.get("干支"), + "zodiac": info_raw.get("生肖"), + "solar_term": info_raw.get("節氣"), + }) + except Exception: + continue + + return results_list + + +@app.get("/leap-info") +def leap_info_endpoint(year: str = Query(None, description="西曆年份,數字")): + """查詢指定西曆年份是否有農曆閏月,以及是閏幾月。""" + if not year: + return JSONResponse({"error": "缺少參數 year"}, status_code=400) + try: + y = int(year) + leap_month = conv.get_leap_month(y) + return {"year": y, "leap_month": leap_month} + except ValueError: + return JSONResponse({"error": "參數 year 必須是有效的整數"}, status_code=400) + except Exception as e: + return JSONResponse({"error": str(e)}, status_code=500) + + +if __name__ == "__main__": + try: + free_port = find_free_port() + print(f"找到一個可用的 port: {free_port}") + uvicorn.run(app, host="0.0.0.0", port=free_port, reload=False) + except Exception as e: + print(f"啟動服務失敗: {e}") diff --git a/chrono_converter.py b/chrono_converter.py new file mode 100644 index 0000000..e16c10b --- /dev/null +++ b/chrono_converter.py @@ -0,0 +1,213 @@ +from datetime import date + +class ChronoConverter: + # 太平天國 + TP_START = (1851, 2, 1) + TP_END = (1872, 7, 5) + + # 清朝年號區間(含康熙到宣統) + KX_START = date(1662, 2, 18) + KX_END = date(1723, 2, 4) + + YZ_START = date(1723, 2, 5) + YZ_END = date(1736, 2, 11) + + QL_START = date(1736, 2, 12) + QL_END = date(1796, 2, 8) + + JQ_START = date(1796, 2, 9) + JQ_END = date(1821, 2, 2) + + DG_START = date(1821, 2, 3) + DG_END = date(1851, 1, 31) + + XF_START = date(1851, 2, 1) + XF_END = date(1862, 1, 29) + + TZ_START = date(1862, 1, 30) + TZ_END = date(1875, 2, 5) + + GX_START = date(1875, 2, 6) + GX_END = date(1909, 1, 21) + + XT_START = date(1909, 1, 22) + XT_END = date(1912, 2, 17) + + # 清太祖天命 & 順治 + TN_START = date(1616, 2, 17) + TN_END = date(1627, 2, 15) + + SZ_START = date(1644, 2, 8) + SZ_END = date(1662, 2, 17) + + # 明朝年號 + WL_START = date(1573, 1, 30) + WL_END = date(1620, 8, 18) + + TQ_START = date(1620, 8, 19) + TQ_END = date(1627, 9, 29) + + CZ_START = date(1628, 2, 5) + CZ_END = date(1644, 4, 25) + + @staticmethod + def to_minguo(year: int) -> str: + if year < 1912: + return f"西元 {year} 年,尚未進入民國" + return f"民國 {year - 1911} 年" + + @staticmethod + def to_ad_from_minguo(minguo_year: int) -> str: + if minguo_year <= 0: + return "民國元年從西元 1912 年開始,不能為 0 或負數" + return f"西元 {minguo_year + 1911} 年" + + @classmethod + def to_taiping(cls, year: int, month: int = 1, day: int = 1) -> str: + if (year, month, day) < cls.TP_START: + return f"西元 {year} 年 {month} 月 {day} 日尚未進入太平天國年間" + elif (year, month, day) > cls.TP_END: + return f"西元 {year} 年 {month} 月 {day} 日已超過太平天國年間" + else: + tp_year = year - 1850 + return f"太平天國 {tp_year} 年" + + @staticmethod + def to_ad_from_taiping(tp_year: int) -> str: + if 1 <= tp_year <= 22: + return f"西元 {tp_year + 1850} 年(約太平天國 {tp_year} 年)" + else: + return f"太平天國 {tp_year} 年超出有效範圍(1~22)" + + @classmethod + def to_tianming(cls, y: int, m: int = 1, d: int = 1) -> str: + try: + input_date = date(y, m, d) + except ValueError: + return "無效的日期" + + if input_date < cls.TN_START: + return f"西元 {y} 年 {m} 月 {d} 日尚未進入天命年間" + elif input_date > cls.TN_END: + return f"西元 {y} 年 {m} 月 {d} 日已超過天命年間" + else: + return f"清太祖天命 {y - 1615} 年" + + @staticmethod + def to_ad_from_tianming(tn_year: int) -> str: + if 1 <= tn_year <= 12: + return f"西元 {tn_year + 1615} 年(約天命 {tn_year} 年)" + else: + return "天命年間僅有 1~12 年" + + @classmethod + def to_shunzhi(cls, y: int, m: int = 1, d: int = 1) -> str: + try: + input_date = date(y, m, d) + except ValueError: + return "無效的日期" + + if input_date < cls.SZ_START: + return f"西元 {y} 年 {m} 月 {d} 日尚未進入順治年間" + elif input_date > cls.SZ_END: + return f"西元 {y} 年 {m} 月 {d} 日已超過順治年間" + else: + return f"清世祖順治 {y - 1643} 年" + + @staticmethod + def to_ad_from_shunzhi(sz_year: int) -> str: + if 1 <= sz_year <= 18: + return f"西元 {sz_year + 1643} 年(約順治 {sz_year} 年)" + else: + return "順治年間僅有 1~18 年" + + @classmethod + def to_qing_dynasty(cls, y: int, m: int = 1, d: int = 1) -> str: + try: + input_date = date(y, m, d) + except ValueError: + return "無效的日期" + + ranges = [ + (cls.KX_START, cls.KX_END, 1661, "清聖祖康熙", 1), + (cls.YZ_START, cls.YZ_END, 1722, "清世宗雍正", 1), + (cls.QL_START, cls.QL_END, 1735, "清高宗乾隆", 1), + (cls.JQ_START, cls.JQ_END, 1795, "清仁宗嘉慶", 1), + (cls.DG_START, cls.DG_END, 1820, "清宣宗道光", 1), + (cls.XF_START, cls.XF_END, 1850, "清文宗咸豐", 1), + (cls.TZ_START, cls.TZ_END, 1861, "清穆宗同治", 1), + (cls.GX_START, cls.GX_END, 1874, "清德宗光緒", 1), + (cls.XT_START, cls.XT_END, 1908, "清遜帝宣統", 1), + ] + + for start, end, base_year, label, base_era in ranges: + if start <= input_date <= end: + return f"{label} {y - base_year + base_era} 年" + + return f"西元 {y} 年 {m} 月 {d} 日不在清代指定年號範圍內" + + @classmethod + def to_ming_dynasty(cls, y: int, m: int = 1, d: int = 1) -> str: + try: + input_date = date(y, m, d) + except ValueError: + return "無效的日期" + + ranges = [ + (cls.WL_START, cls.WL_END, 1572, "明神宗萬曆", 1), + (cls.TQ_START, cls.TQ_END, 1619, "明熹宗天啟", 1), + (cls.CZ_START, cls.CZ_END, 1627, "明思宗崇禎", 1), + ] + + for start, end, base_year, label, base_era in ranges: + if start <= input_date <= end: + return f"{label} {y - base_year + base_era} 年" + + return f"西元 {y} 年 {m} 月 {d} 日不在明代指定年號範圍內" + + @staticmethod + def weekday_str(y: int, m: int, d: int) -> str: + try: + dt = date(y, m, d) + return dt.strftime("%A") + except ValueError: + return "無效日期" + + +# 檢查當前程式是否被作為主程式執行 +if __name__ == "__main__": + + + def convert_date(y: int, m: int, d: int) -> dict: + cc = ChronoConverter() + raw = { + "民國": cc.to_minguo(y), + "太平天國": cc.to_taiping(y, m, d), + "清太祖天命": cc.to_tianming(y, m, d), + "清世祖順治": cc.to_shunzhi(y, m, d), + "清朝其他年號": cc.to_qing_dynasty(y, m, d), + "明朝晚期年號": cc.to_ming_dynasty(y, m, d), + "星期": cc.weekday_str(y, m, d), + } + + # 過濾掉無效的年號資訊 + filtered = { + k: v for k, v in raw.items() + if not ( + "尚未進入" in v or + "已超過" in v or + "不在" in v or + "無效" in v + ) + } + + return filtered + + + # result = convert_date(1851, 2, 1) + # result = convert_date(1977, 10, 12) + result = convert_date(1906, 2, 7) + # result = convert_date(1967, 10, 17) + print(result) + for key, val in result.items(): + print(f"{key}:{val}") diff --git a/converter.py b/converter.py new file mode 100644 index 0000000..89d4d1d --- /dev/null +++ b/converter.py @@ -0,0 +1,107 @@ +# converter.py +import os +import json +from datetime import datetime +from typing import List, Optional + +class Converter: + """ + 提供西曆與農曆互轉功能,並能判斷特定年份的閏月。 + """ + LUNAR_JSON_DIR = 'lunar_json' + + def __init__(self): + """初始化時,建立一個空的快取來存放已查詢過的年份閏月資訊。""" + self._leap_month_cache = {} + + @classmethod + def load_solar_to_lunar(cls, year: int) -> dict: + """這是一個類別方法,用來載入指定年份的資料檔。""" + path = os.path.join(cls.LUNAR_JSON_DIR, f"{year}.json") + if not os.path.exists(path): + raise FileNotFoundError(f"找不到農曆資料檔: {path}") + with open(path, encoding='utf-8') as f: + return json.load(f) + + def solar_to_lunar(self, solar_date_str: str) -> dict: + try: + dt = datetime.strptime(solar_date_str, '%Y-%m-%d').date() + except ValueError: + raise ValueError("西曆日期格式應為 YYYY-MM-DD") + # 【修正點】從 self. 呼叫改為 Converter. 呼叫 + data = Converter.load_solar_to_lunar(dt.year) + if solar_date_str not in data: + raise KeyError(f"沒有找到對應 {solar_date_str} 的農曆資料") + return data[solar_date_str] + + def lunar_to_solar(self, lunar_year: int, lunar_month: int, lunar_day: int, leap: bool=False) -> str: + years_to_check = {lunar_year, lunar_year + 1} + for year in sorted(list(years_to_check)): + try: + # 【修正點】從 self. 呼叫改為 Converter. 呼叫 + data = Converter.load_solar_to_lunar(year) + for solar_date, info in data.items(): + lstr = info.get('農曆', '') + is_leap = lstr.startswith('閏') + core = lstr[1:] if is_leap else lstr + if not all(c in core for c in ['月', '日']): continue + m_str, d_str = core.replace('月','-').replace('日','').split('-') + if int(m_str) == lunar_month and int(d_str) == lunar_day and is_leap == leap: + return info.get("西曆") + except FileNotFoundError: + continue + raise KeyError(f"找不到對應農曆 {lunar_year}{'閏' if leap else ''}{lunar_month}月{lunar_day}日 的西曆") + + def lunar_to_solar_all(self, lunar_year: int, lunar_month: int, lunar_day: int) -> List[str]: + results = [] + try: + solar_date = self.lunar_to_solar(lunar_year, lunar_month, lunar_day, leap=False) + results.append(solar_date) + except KeyError: + pass + try: + solar_date = self.lunar_to_solar(lunar_year, lunar_month, lunar_day, leap=True) + results.append(solar_date) + except KeyError: + pass + return results + + def get_leap_month(self, year: int) -> Optional[int]: + if year in self._leap_month_cache: + return self._leap_month_cache[year] + try: + # 【修正點】從 self. 呼叫改為 Converter. 呼叫 + data = Converter.load_solar_to_lunar(year) + for info in data.values(): + lstr = info.get('農曆', '') + if lstr.startswith('閏'): + core = lstr[1:] + if '月' in core: + month_str = core.split('月')[0] + leap_month = int(month_str) + self._leap_month_cache[year] = leap_month + return leap_month + self._leap_month_cache[year] = None + return None + except FileNotFoundError: + self._leap_month_cache[year] = None + return None + +# ... (main 函式不變) ... +def main(): + """展示 Converter 的新功能:查詢閏月""" + print("=== Converter 閏月判斷功能展示 ===") + try: + converter = Converter() + years_to_test = [1976, 1977, 2023, 2024, 2025] + for year in years_to_test: + leap_month = converter.get_leap_month(year) + if leap_month: + print(f"西曆 {year} 年,有閏月,是閏 {leap_month} 月。") + else: + print(f"西曆 {year} 年,沒有閏月。") + except Exception as e: + print(f"\n發生錯誤: {e}") + +if __name__ == '__main__': + main() diff --git a/converter_with_eras.py b/converter_with_eras.py new file mode 100644 index 0000000..2fa5614 --- /dev/null +++ b/converter_with_eras.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# converter_with_eras.py +# +# 結合「西曆↔農曆轉換」與「歷代年號 ↔ 西曆」功能 +# 支援:太平天國、民國、中國明清諸年號(農曆基準)、日本各元號(西曆基準) + +import os +import sys +import json +import argparse +from datetime import datetime, date +from typing import Dict + +# 中國明清年號對應的西曆範圍及基準年 (起始日, 結束日, 基年) +CHINESE_ERAS_SOLAR = { + # 明朝 + "萬曆": (date(1573, 1, 1), date(1620, 12, 31), 1572), + "天啟": (date(1621, 1, 1), date(1627, 12, 31), 1620), + "崇禎": (date(1628, 1, 1), date(1644, 4, 25), 1627), # 崇禎最後一天 1644-04-25 + # 清朝 + "順治": (date(1644, 2, 8), date(1662, 2, 17), 1643), # 順治 1年 1644-02-08 至 1662-02-17 + "康熙": (date(1662, 2, 18), date(1723, 2, 8), 1661), # 康熙 1年 1662-02-18 至 1723-02-08 + "雍正": (date(1723, 2, 9), date(1736, 1, 28), 1722), # 雍正 1年 1723-02-09 至 1736-01-28 + "乾隆": (date(1736, 1, 29), date(1796, 2, 17), 1735), # 乾隆 1年 1736-01-29 至 1796-02-17 + "嘉慶": (date(1796, 2, 18), date(1821, 2, 6), 1795), # 嘉慶 1年 1796-02-18 至 1821-02-06 + "道光": (date(1821, 2, 7), date(1851, 1, 27), 1820), # 道光 1年 1821-02-07 至 1851-01-27 + "咸豐": (date(1851, 1, 28), date(1862, 1, 16), 1850), # 咸豐 1年 1851-01-28 至 1862-01-16 + "同治": (date(1862, 1, 17), date(1875, 2, 5), 1861), # 同治 1年 1862-01-17 至 1875-02-05 + "光緒": (date(1875, 2, 6), date(1909, 1, 22), 1874), # 光緒 1年 1875-02-06 至 1909-01-22 + "宣統": (date(1909, 1, 23), date(1912, 2, 18), 1908), # 宣統 1年 1909-01-23 至 1912-02-18 +} + +class Converter: + """ + 提供西曆與農曆互轉功能: + - solar_to_lunar(date_str: str) -> dict + - lunar_to_solar(year: int, month: int, day: int, leap: bool=False) -> str + """ + LUNAR_JSON_DIR = 'lunar_json' + + @classmethod + def load_solar_to_lunar(cls, year: int) -> Dict: + path = os.path.join(cls.LUNAR_JSON_DIR, f"{year}.json") + if not os.path.exists(path): + raise FileNotFoundError(f"找不到 {path}") + with open(path, encoding='utf-8') as f: + return json.load(f) + + def solar_to_lunar(self, solar_date_str: str) -> Dict: + # 允許輸入 "YYYY-M-D",並自動標準化為 ISO 格式 "YYYY-MM-DD" + parts = solar_date_str.split('-') + if len(parts) != 3: + raise ValueError(f"日期格式錯誤,請使用 YYYY-MM-DD 或類似格式:{solar_date_str}") + y, m, d = map(int, parts) + dt = date(y, m, d) + key = dt.isoformat() + try: + data = self.load_solar_to_lunar(dt.year) + except FileNotFoundError: + raise FileNotFoundError(f"無法取得 {dt.year} 年的農曆資料,請先生成對應的 lunar_json/{dt.year}.json 檔案") + if key not in data: + raise KeyError(f"沒有找到對應 {key} 的農曆資料") + return data[key] + + def lunar_to_solar(self, lunar_year: int, lunar_month: int, lunar_day: int, leap: bool=False) -> str: + solar_year = lunar_year + 1 if lunar_month in (11, 12) else lunar_year + data = self.load_solar_to_lunar(solar_year) + # 精確匹配 + for solar, info in data.items(): + lstr = info.get('農曆','') + is_leap = lstr.startswith('閏') + core = lstr[1:] if is_leap else lstr + m_str, d_str = core.replace('月','-').replace('日','').split('-') + if int(m_str)==lunar_month and int(d_str)==lunar_day and is_leap==leap: + return solar + # 回退匹配非閏月 + for solar, info in data.items(): + lstr = info.get('農曆','') + is_leap = lstr.startswith('閏') + core = lstr[1:] if is_leap else lstr + m_str, d_str = core.replace('月','-').replace('日','').split('-') + if int(m_str)==lunar_month and int(d_str)==lunar_day and not is_leap: + return solar + raise KeyError(f"找不到對應農曆 {lunar_year}{'閏' if leap else ''}{lunar_month}月{lunar_day}日 的西曆") + +class ChronoConverter: + """ + 統一各種紀年 ↔ 西曆 的轉換 + """ + # 日本元號對應的西曆範圍及基準年 (起始日, 結束日, 基年) + JAPANESE_ERAS = { + "慶應": (date(1865,3,18), date(1868,10,22), 1864), + "明治": (date(1868,10,23), date(1912,7,30), 1867), + "大正": (date(1912,7,30), date(1926,12,24), 1911), + "昭和": (date(1926,12,25), date(1989,1,7), 1925), + "平成": (date(1989,1,8), date(2019,4,30), 1988), + "令和": (date(2019,5,1), None, 2018), + } + + @staticmethod + def _lunar_year(y: int, m: int, d: int) -> int: + conv = Converter() + dt = date(y, m, d) + for ly in (y, y-1): + try: + start = conv.lunar_to_solar(ly, 1, 1) + nxt = conv.lunar_to_solar(ly+1, 1, 1) + except Exception: + continue + d0, d1 = date.fromisoformat(start), date.fromisoformat(nxt) + if d0 <= dt < d1: + return ly + return y + + @classmethod + def to_era(cls, y: int, m: int, d: int) -> Dict: + dt = date(y, m, d) + ly = cls._lunar_year(y, m, d) + res: Dict[str, str] = {} + + if date(1851,2,1) <= dt <= date(1872,7,5): + res["太平天國"] = f"太平天國 {y-1850} 年" + if y >= 1912: + res["民國"] = f"民國 {y-1911} 年" + for era, (st, ed, base) in CHINESE_ERAS_SOLAR.items(): + if st <= dt <= ed: + res[era] = f"{era} {dt.year - base} 年" + for era, (st, ed, base) in cls.JAPANESE_ERAS.items(): + if dt >= st and (ed is None or dt <= ed): + res[era] = f"{era} {dt.year-base} 年" + res["星期"] = dt.strftime("%A") + return res + + +def convert_date(y: int, m: int, d: int) -> Dict: + return ChronoConverter.to_era(y, m, d) + + +def main(): + parser = argparse.ArgumentParser(description="西曆↔農曆+歷代年號+日本元號 轉換工具") + sub = parser.add_subparsers(dest='cmd', required=True) + + p1 = sub.add_parser('solar2lunar', help='西曆轉農曆') + p1.add_argument('date', help='YYYY-MM-DD 或 YYYY-M-D') + + p2 = sub.add_parser('lunar2solar', help='農曆轉西曆') + p2.add_argument('date', help='YYYY-MM-DD') + p2.add_argument('--leap', action='store_true', help='閏月') + + p3 = sub.add_parser('era', help='西曆轉歷代年號+日本元號') + p3.add_argument('date', help='YYYY-MM-DD') + + args = parser.parse_args() + if args.cmd == 'solar2lunar': + try: + result = Converter().solar_to_lunar(args.date) + print(json.dumps(result, ensure_ascii=False, indent=2)) + except (FileNotFoundError, KeyError, ValueError) as e: + print(e) + sys.exit(1) + elif args.cmd == 'lunar2solar': + try: + y, m, d = map(int, args.date.split('-',2)) + solar = Converter().lunar_to_solar(y, m, d, args.leap) + print(json.dumps(Converter().solar_to_lunar(solar), ensure_ascii=False, indent=2)) + except Exception as e: + print(e) + sys.exit(1) + elif args.cmd == 'era': + y, m, d = map(int, args.date.split('-',2)) + print(json.dumps(convert_date(y, m, d), ensure_ascii=False, indent=2)) + +if __name__ == '__main__': + main() diff --git a/generate_lunar_calendar_json.py b/generate_lunar_calendar_json.py new file mode 100644 index 0000000..dee9513 --- /dev/null +++ b/generate_lunar_calendar_json.py @@ -0,0 +1,29 @@ +import os +from multiprocessing import Pool +from pathlib import Path +from lunar_calendar import LunarCalendar + +def process_year(year, output_dir): + try: + cal = LunarCalendar(year) + out_file = output_dir / f"{year}.json" + # 如果不想覆寫,再加個 exists() 檢查 + if not out_file.exists(): + cal.save_json(str(out_file)) + print(f"完成,已輸出:{out_file}") + else: + print(f"跳過,檔案已存在:{out_file}") + except Exception as e: + print(f"處理 {year} 時出錯:{e}") + +def main(): + output_dir = Path('lunar_json') + output_dir.mkdir(parents=True, exist_ok=True) + + # years = range(1851, 2149) + years = range(1, 2149) + with Pool() as pool: + pool.starmap(process_year, [(y, output_dir) for y in years]) + +if __name__ == "__main__": + main() diff --git a/lunar_calendar.py b/lunar_calendar.py new file mode 100644 index 0000000..117c8e2 --- /dev/null +++ b/lunar_calendar.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.13" +# dependencies = [ +# "skyfield", +# "jplephem", +# "numpy", +# ] +# /// + +import json +from datetime import date, timedelta +from typing import Any, cast + +from skyfield.almanac import find_discrete, moon_phases +from skyfield.api import load +from skyfield.framelib import ecliptic_frame + + +class LunarCalendar: + """利用 Skyfield 計算農曆、干支、生肖、節氣並輸出 JSON。""" + + TZ_OFFSET = timedelta(hours=8) # 台北時區 + + HEAVENLY_STEMS = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"] + EARTHLY_BRANCHES = [ + "子", + "丑", + "寅", + "卯", + "辰", + "巳", + "午", + "未", + "申", + "酉", + "戌", + "亥", + ] + ZODIAC = { + "子": "鼠", + "丑": "牛", + "寅": "虎", + "卯": "兔", + "辰": "龍", + "巳": "蛇", + "午": "馬", + "未": "羊", + "申": "猴", + "酉": "雞", + "戌": "狗", + "亥": "豬", + } + SOLAR_TERMS_24 = { + 315: "立春", + 330: "雨水", + 345: "驚蟄", + 0: "春分", + 15: "清明", + 30: "穀雨", + 45: "立夏", + 60: "小滿", + 75: "芒種", + 90: "夏至", + 105: "小暑", + 120: "大暑", + 135: "立秋", + 150: "處暑", + 165: "白露", + 180: "秋分", + 195: "寒露", + 210: "霜降", + 225: "立冬", + 240: "小雪", + 255: "大雪", + 270: "冬至", + 285: "小寒", + 300: "大寒", + } + + def __init__(self, year: int): + self.year = year + self.ts = load.timescale() + # Ephemeris kernel options: + # - de422.bsp: default here as it is bundled in this repo. + # - de440s.bsp: newer, compact; suitable for 1900–2150 use-cases. + # - de421.bsp: older but still usable for compatibility. + # Switch by uncommenting one of the lines below and ensure the file + # exists in the working directory (or provide an absolute path). + # self.eph = load('de421.bsp') + # self.eph = load('de440s.bsp') + self.eph = load("de422.bsp") + # Pylance sometimes mis-infers types. + # Cast to Any to avoid false positives. + self.earth = cast(Any, self.eph["earth"]) # has method .at(t) + self.sun = cast(Any, self.eph["sun"]) # used with .observe() + + def _compute_astronomy(self): + """抓新朔與 24 節氣時間點""" + t0 = self.ts.utc(self.year - 1, 11, 1) + t1 = self.ts.utc(self.year + 1, 3, 1) + + # 每月朔 + times, phases = find_discrete(t0, t1, moon_phases(self.eph)) + new_moons = [t for t, p in zip(times, phases, strict=False) if p == 0] + + # 每 15° 的節氣 + def term_index(t): + lat, lon, _ = ( + self.earth.at(t) + .observe(self.sun) + .apparent() + .frame_latlon(ecliptic_frame) + ) + return (lon.degrees // 15).astype(int) + + term_index.step_days = 1 + + st_times, st_idxs = find_discrete(t0, t1, term_index) + zhongqi = [ + (t, (idx * 15) % 360) for t, idx in zip(st_times, st_idxs, strict=False) + ] + + return new_moons, zhongqi + + def _label_months(self, new_moons, zhongqi): + """根據冬至、中氣規則標記每段朔月的月號與閏月屬性""" + cutoff = date(self.year, 1, 1) + sols = [ + t + for t, deg in zhongqi + if deg == 270 and (t.utc_datetime() + self.TZ_OFFSET).date() < cutoff + ] + solstice = max(sols) + i0 = max( + i + for i, t in enumerate(new_moons) + if t.utc_datetime() < solstice.utc_datetime() + ) + + labels = [] + month_no = 11 + leap_used = False + + for j in range(i0, len(new_moons) - 1): + start, end = new_moons[j], new_moons[j + 1] + cnt = sum(1 for t, deg in zhongqi if start < t < end and deg % 30 == 0) + + if j == i0: + labels.append((start, end, 11, False)) + else: + if cnt == 0 and not leap_used: + labels.append((start, end, month_no, True)) + leap_used = True + else: + month_no = month_no % 12 + 1 + labels.append((start, end, month_no, False)) + + return labels + + def _ganzhi_year(self, y): + """計算指定年之干支""" + s = self.HEAVENLY_STEMS[(y - 4) % 10] + b = self.EARTHLY_BRANCHES[(y - 4) % 12] + return s + b + + def build_calendar(self): + """產生從 YYYY-01-01 至 YYYY-12-31 的完整農曆對照表""" + new_moons, zhongqi = self._compute_astronomy() + month_labels = self._label_months(new_moons, zhongqi) + + # 節氣快查 + solar_terms = {} + for t, deg in zhongqi: + d = (t.utc_datetime() + self.TZ_OFFSET).date().isoformat() + name = self.SOLAR_TERMS_24.get(deg) + if name: + solar_terms[d] = name + + result = {} + day = date(self.year, 1, 1) + end = date(self.year, 12, 31) + + while day <= end: + for ts, te, m, is_leap in month_labels: + ds = (ts.utc_datetime() + self.TZ_OFFSET).date() + de = (te.utc_datetime() + self.TZ_OFFSET).date() + if ds <= day < de: + # 農曆年:11–12 月屬上一農曆年,其它屬當年 + lunar_year = self.year if 1 <= m <= 10 else self.year - 1 + gz_year = self._ganzhi_year(lunar_year) + zodiac = self.ZODIAC[self.EARTHLY_BRANCHES[(lunar_year - 4) % 12]] + + offset = (day - ds).days + 1 + lunar_str = f"{'閏' if is_leap else ''}{m}月{offset}日" + result[day.isoformat()] = { + "西曆": day.isoformat(), + "農曆": lunar_str, + "干支": gz_year, + "生肖": zodiac, + "節氣": solar_terms.get(day.isoformat(), ""), + } + break + day += timedelta(days=1) + + return result + + def save_json(self, filename: str | None = None): + """將該年日曆輸出為 JSON 檔""" + data = self.build_calendar() + fname = filename or f"{self.year}.json" + with open(fname, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + print(f"完成!已輸出:{fname}") + + +if __name__ == "__main__": + import sys + + if len(sys.argv) != 2 or not sys.argv[1].isdigit(): + print("用法:python3 lunar_calendar.py <年份>") + sys.exit(1) + year = int(sys.argv[1]) + cal = LunarCalendar(year) + cal.save_json() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8d90d7a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[project] +name = "lunar-converter" +version = "0.1.0" +requires-python = ">=3.10" +dependencies = [ + "skyfield", + "jplephem", + "numpy", + "fastapi", + "uvicorn", + "loguru", +] + +[tool.pytest.ini_options] +addopts = "-q" +testpaths = ["tests"] diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..462e770 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,14 @@ +{ + // Pylance/Pyright 設定:用來指定 Python 語言等級與檢查行為 + // 注意:這是正確的位置,VS Code 設定中沒有 `python.analysis.pythonVersion` 此鍵 + "pythonVersion": "3.11", + "typeCheckingMode": "basic", + "diagnosticSeverityOverrides": { + // 若已在 .vscode/settings.json 關閉這兩者,可移除以下兩行 + "reportMissingImports": "none", + "reportMissingModuleSource": "none" + }, + "executionEnvironments": [ + { "root": ".", "pythonVersion": "3.11" } + ] +} diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..2fc1f22 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,33 @@ +line-length = 100 +target-version = "py310" + +# 忽略常見產物與虛擬環境資料夾 +extend-exclude = [ + ".venv", + "venv", + ".tox", + ".mypy_cache", + ".pytest_cache", + ".ruff_cache", + "build", + "dist", + "node_modules", +] + +[lint] +# 基本錯誤/風格 + 匯入排序 + pyupgrade + bugbear +select = ["E", "F", "I", "UP", "B"] +# 允許自動修復所有可修復項目(VS Code 會透過 Code Action 觸發) +fixable = ["ALL"] + +[lint.isort] +# 預設與 Black 兼容的行為已良好,一般無需額外調整 + +[format] +# 格式器偏好(VS Code 也設定了 LF 與 ruler 88) +quote-style = "double" # 可選:"single" | "double" | "preserve" +indent-style = "space" # 可選:"space" | "tab" +line-ending = "lf" # 可選:"lf" | "cr-lf" | "auto" +# 若文件含有 docstring 內嵌程式碼,可一併格式化 +docstring-code-format = true +docstring-code-line-length = 100 diff --git a/run_uvicorn.sh b/run_uvicorn.sh new file mode 100644 index 0000000..cd895da --- /dev/null +++ b/run_uvicorn.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# uv run uvicorn api:app --host 0.0.0.0 --port 0 +# uv run uvicorn api:app --host 0.0.0.0 --port 5001 +uv run uvicorn api:app --host 0.0.0.0 --port 53511 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..34072b9 --- /dev/null +++ b/uv.lock @@ -0,0 +1,524 @@ +version = 1 +revision = 2 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version < '3.11'", +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/12/e5e0282d673bb9746bacfb6e2dba8719989d3660cdb2ea79aee9a9651afb/anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1", size = 107213, upload-time = "2025-08-04T08:54:24.882Z" }, +] + +[[package]] +name = "certifi" +version = "2025.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342, upload-time = "2025-05-20T23:19:49.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215, upload-time = "2025-05-20T23:19:47.796Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "fastapi" +version = "0.116.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "jplephem" +version = "2.23" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/42/0545c37e070e5f940eb52987fb25a351ab3c9327b40bab6ad3dd6125b2e1/jplephem-2.23.tar.gz", hash = "sha256:d3fb9477e4bf4c39d10497d4ff15e5271b7ac03fa101e1821aac527d646eccf9", size = 44989, upload-time = "2025-06-22T18:42:28.813Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/e0/b215df7cae3be61a0a293346723f5b068e359a3430dcadb32ef90ae3398c/jplephem-2.23-py3-none-any.whl", hash = "sha256:66110814810b0b30213bdd058a85a41bedbc030bb8a365c85088674fe02546cf", size = 49368, upload-time = "2025-06-22T18:42:27.642Z" }, +] + +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, +] + +[[package]] +name = "lunar-converter" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "fastapi" }, + { name = "jplephem" }, + { name = "loguru" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "skyfield" }, + { name = "uvicorn" }, +] + +[package.metadata] +requires-dist = [ + { name = "fastapi" }, + { name = "jplephem" }, + { name = "loguru" }, + { name = "numpy" }, + { name = "skyfield" }, + { name = "uvicorn" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253, upload-time = "2025-09-09T15:56:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980, upload-time = "2025-09-09T15:56:05.926Z" }, + { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709, upload-time = "2025-09-09T15:56:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923, upload-time = "2025-09-09T15:56:09.443Z" }, + { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591, upload-time = "2025-09-09T15:56:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714, upload-time = "2025-09-09T15:56:14.637Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592, upload-time = "2025-09-09T15:56:17.285Z" }, + { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474, upload-time = "2025-09-09T15:56:20.943Z" }, + { url = "https://files.pythonhosted.org/packages/45/fa/7f43ba10c77575e8be7b0138d107e4f44ca4a1ef322cd16980ea3e8b8222/numpy-2.3.3-cp311-cp311-win32.whl", hash = "sha256:eb63d443d7b4ffd1e873f8155260d7f58e7e4b095961b01c91062935c2491e57", size = 6599794, upload-time = "2025-09-09T15:56:23.258Z" }, + { url = "https://files.pythonhosted.org/packages/0a/a2/a4f78cb2241fe5664a22a10332f2be886dcdea8784c9f6a01c272da9b426/numpy-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:ec9d249840f6a565f58d8f913bccac2444235025bbb13e9a4681783572ee3caa", size = 13088104, upload-time = "2025-09-09T15:56:25.476Z" }, + { url = "https://files.pythonhosted.org/packages/79/64/e424e975adbd38282ebcd4891661965b78783de893b381cbc4832fb9beb2/numpy-2.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:74c2a948d02f88c11a3c075d9733f1ae67d97c6bdb97f2bb542f980458b257e7", size = 10460772, upload-time = "2025-09-09T15:56:27.679Z" }, + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, + { url = "https://files.pythonhosted.org/packages/17/b6/fc8f82cb3520768718834f310c37d96380d9dc61bfdaf05fe5c0b7653e01/numpy-2.3.3-cp312-cp312-win32.whl", hash = "sha256:5534ed6b92f9b7dca6c0a19d6df12d41c68b991cef051d108f6dbff3babc4ebf", size = 6320800, upload-time = "2025-09-09T15:56:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/32/ee/de999f2625b80d043d6d2d628c07d0d5555a677a3cf78fdf868d409b8766/numpy-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:497d7cad08e7092dba36e3d296fe4c97708c93daf26643a1ae4b03f6294d30eb", size = 12786615, upload-time = "2025-09-09T15:56:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/49/6e/b479032f8a43559c383acb20816644f5f91c88f633d9271ee84f3b3a996c/numpy-2.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:ca0309a18d4dfea6fc6262a66d06c26cfe4640c3926ceec90e57791a82b6eee5", size = 10195936, upload-time = "2025-09-09T15:56:56.541Z" }, + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/487a2bccbf7cc9d4bfc5f0f197761a5ef27ba870f1e3bbb9afc4bbe3fcc2/numpy-2.3.3-cp313-cp313-win32.whl", hash = "sha256:9591e1221db3f37751e6442850429b3aabf7026d3b05542d102944ca7f00c8a8", size = 6312250, upload-time = "2025-09-09T15:57:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b5/263ebbbbcede85028f30047eab3d58028d7ebe389d6493fc95ae66c636ab/numpy-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f0dadeb302887f07431910f67a14d57209ed91130be0adea2f9793f1a4f817cf", size = 12783269, upload-time = "2025-09-09T15:57:23.034Z" }, + { url = "https://files.pythonhosted.org/packages/fa/75/67b8ca554bbeaaeb3fac2e8bce46967a5a06544c9108ec0cf5cece559b6c/numpy-2.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:3c7cf302ac6e0b76a64c4aecf1a09e51abd9b01fc7feee80f6c43e3ab1b1dbc5", size = 10195314, upload-time = "2025-09-09T15:57:25.045Z" }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8e/3ab61a730bdbbc201bb245a71102aa609f0008b9ed15255500a99cd7f780/numpy-2.3.3-cp313-cp313t-win32.whl", hash = "sha256:a333b4ed33d8dc2b373cc955ca57babc00cd6f9009991d9edc5ddbc1bac36bcd", size = 6442776, upload-time = "2025-09-09T15:57:45.793Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3a/e22b766b11f6030dc2decdeff5c2fb1610768055603f9f3be88b6d192fb2/numpy-2.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:4384a169c4d8f97195980815d6fcad04933a7e1ab3b530921c3fef7a1c63426d", size = 12927281, upload-time = "2025-09-09T15:57:47.492Z" }, + { url = "https://files.pythonhosted.org/packages/7b/42/c2e2bc48c5e9b2a83423f99733950fbefd86f165b468a3d85d52b30bf782/numpy-2.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:75370986cc0bc66f4ce5110ad35aae6d182cc4ce6433c40ad151f53690130bf1", size = 10265275, upload-time = "2025-09-09T15:57:49.647Z" }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a1/b87a284fb15a42e9274e7fcea0dad259d12ddbf07c1595b26883151ca3b4/numpy-2.3.3-cp314-cp314-win32.whl", hash = "sha256:cb248499b0bc3be66ebd6578b83e5acacf1d6cb2a77f2248ce0e40fbec5a76d0", size = 6366952, upload-time = "2025-09-09T15:58:10.096Z" }, + { url = "https://files.pythonhosted.org/packages/70/5f/1816f4d08f3b8f66576d8433a66f8fa35a5acfb3bbd0bf6c31183b003f3d/numpy-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:691808c2b26b0f002a032c73255d0bd89751425f379f7bcd22d140db593a96e8", size = 12919322, upload-time = "2025-09-09T15:58:12.138Z" }, + { url = "https://files.pythonhosted.org/packages/8c/de/072420342e46a8ea41c324a555fa90fcc11637583fb8df722936aed1736d/numpy-2.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:9ad12e976ca7b10f1774b03615a2a4bab8addce37ecc77394d8e986927dc0dfe", size = 10478630, upload-time = "2025-09-09T15:58:14.64Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e5ed830e08cd0196351db55db82f65bc0ab05da6ef2b72a836dcf1936d2f/numpy-2.3.3-cp314-cp314t-win32.whl", hash = "sha256:1250c5d3d2562ec4174bce2e3a1523041595f9b651065e4a4473f5f48a6bc8a5", size = 6515371, upload-time = "2025-09-09T15:58:36.04Z" }, + { url = "https://files.pythonhosted.org/packages/47/c7/b0f6b5b67f6788a0725f744496badbb604d226bf233ba716683ebb47b570/numpy-2.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b37a0b2e5935409daebe82c1e42274d30d9dd355852529eab91dab8dcca7419f", size = 13112576, upload-time = "2025-09-09T15:58:37.927Z" }, + { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019, upload-time = "2025-09-09T15:58:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288, upload-time = "2025-09-09T15:58:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425, upload-time = "2025-09-09T15:58:48.6Z" }, + { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053, upload-time = "2025-09-09T15:58:50.401Z" }, + { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354, upload-time = "2025-09-09T15:58:52.704Z" }, + { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413, upload-time = "2025-09-09T15:58:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload-time = "2025-06-14T08:33:17.137Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195, upload-time = "2025-04-23T18:33:52.104Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/92/b31726561b5dae176c2d2c2dc43a9c5bfba5d32f96f8b4c0a600dd492447/pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", size = 2028817, upload-time = "2025-04-23T18:30:43.919Z" }, + { url = "https://files.pythonhosted.org/packages/a3/44/3f0b95fafdaca04a483c4e685fe437c6891001bf3ce8b2fded82b9ea3aa1/pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", size = 1861357, upload-time = "2025-04-23T18:30:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/30/97/e8f13b55766234caae05372826e8e4b3b96e7b248be3157f53237682e43c/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", size = 1898011, upload-time = "2025-04-23T18:30:47.591Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a3/99c48cf7bafc991cc3ee66fd544c0aae8dc907b752f1dad2d79b1b5a471f/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", size = 1982730, upload-time = "2025-04-23T18:30:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/de/8e/a5b882ec4307010a840fb8b58bd9bf65d1840c92eae7534c7441709bf54b/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", size = 2136178, upload-time = "2025-04-23T18:30:50.907Z" }, + { url = "https://files.pythonhosted.org/packages/e4/bb/71e35fc3ed05af6834e890edb75968e2802fe98778971ab5cba20a162315/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", size = 2736462, upload-time = "2025-04-23T18:30:52.083Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/c8f7593e6bc7066289bbc366f2235701dcbebcd1ff0ef8e64f6f239fb47d/pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", size = 2005652, upload-time = "2025-04-23T18:30:53.389Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7a/996d8bd75f3eda405e3dd219ff5ff0a283cd8e34add39d8ef9157e722867/pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", size = 2113306, upload-time = "2025-04-23T18:30:54.661Z" }, + { url = "https://files.pythonhosted.org/packages/ff/84/daf2a6fb2db40ffda6578a7e8c5a6e9c8affb251a05c233ae37098118788/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", size = 2073720, upload-time = "2025-04-23T18:30:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/2258da019f4825128445ae79456a5499c032b55849dbd5bed78c95ccf163/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", size = 2244915, upload-time = "2025-04-23T18:30:57.501Z" }, + { url = "https://files.pythonhosted.org/packages/d8/7a/925ff73756031289468326e355b6fa8316960d0d65f8b5d6b3a3e7866de7/pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", size = 2241884, upload-time = "2025-04-23T18:30:58.867Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b0/249ee6d2646f1cdadcb813805fe76265745c4010cf20a8eba7b0e639d9b2/pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", size = 1910496, upload-time = "2025-04-23T18:31:00.078Z" }, + { url = "https://files.pythonhosted.org/packages/66/ff/172ba8f12a42d4b552917aa65d1f2328990d3ccfc01d5b7c943ec084299f/pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", size = 1955019, upload-time = "2025-04-23T18:31:01.335Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8d/71db63483d518cbbf290261a1fc2839d17ff89fce7089e08cad07ccfce67/pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", size = 2028584, upload-time = "2025-04-23T18:31:03.106Z" }, + { url = "https://files.pythonhosted.org/packages/24/2f/3cfa7244ae292dd850989f328722d2aef313f74ffc471184dc509e1e4e5a/pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", size = 1855071, upload-time = "2025-04-23T18:31:04.621Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d3/4ae42d33f5e3f50dd467761304be2fa0a9417fbf09735bc2cce003480f2a/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", size = 1897823, upload-time = "2025-04-23T18:31:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f3/aa5976e8352b7695ff808599794b1fba2a9ae2ee954a3426855935799488/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", size = 1983792, upload-time = "2025-04-23T18:31:07.93Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7a/cda9b5a23c552037717f2b2a5257e9b2bfe45e687386df9591eff7b46d28/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", size = 2136338, upload-time = "2025-04-23T18:31:09.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/b8f9ec8dd1417eb9da784e91e1667d58a2a4a7b7b34cf4af765ef663a7e5/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", size = 2730998, upload-time = "2025-04-23T18:31:11.7Z" }, + { url = "https://files.pythonhosted.org/packages/47/bc/cd720e078576bdb8255d5032c5d63ee5c0bf4b7173dd955185a1d658c456/pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", size = 2003200, upload-time = "2025-04-23T18:31:13.536Z" }, + { url = "https://files.pythonhosted.org/packages/ca/22/3602b895ee2cd29d11a2b349372446ae9727c32e78a94b3d588a40fdf187/pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", size = 2113890, upload-time = "2025-04-23T18:31:15.011Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e6/e3c5908c03cf00d629eb38393a98fccc38ee0ce8ecce32f69fc7d7b558a7/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d", size = 2073359, upload-time = "2025-04-23T18:31:16.393Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/6a36a07c59ebefc8777d1ffdaf5ae71b06b21952582e4b07eba88a421c79/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", size = 2245883, upload-time = "2025-04-23T18:31:17.892Z" }, + { url = "https://files.pythonhosted.org/packages/16/3f/59b3187aaa6cc0c1e6616e8045b284de2b6a87b027cce2ffcea073adf1d2/pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", size = 2241074, upload-time = "2025-04-23T18:31:19.205Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ed/55532bb88f674d5d8f67ab121a2a13c385df382de2a1677f30ad385f7438/pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", size = 1910538, upload-time = "2025-04-23T18:31:20.541Z" }, + { url = "https://files.pythonhosted.org/packages/fe/1b/25b7cccd4519c0b23c2dd636ad39d381abf113085ce4f7bec2b0dc755eb1/pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", size = 1952909, upload-time = "2025-04-23T18:31:22.371Z" }, + { url = "https://files.pythonhosted.org/packages/49/a9/d809358e49126438055884c4366a1f6227f0f84f635a9014e2deb9b9de54/pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", size = 1897786, upload-time = "2025-04-23T18:31:24.161Z" }, + { url = "https://files.pythonhosted.org/packages/18/8a/2b41c97f554ec8c71f2a8a5f85cb56a8b0956addfe8b0efb5b3d77e8bdc3/pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", size = 2009000, upload-time = "2025-04-23T18:31:25.863Z" }, + { url = "https://files.pythonhosted.org/packages/a1/02/6224312aacb3c8ecbaa959897af57181fb6cf3a3d7917fd44d0f2917e6f2/pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", size = 1847996, upload-time = "2025-04-23T18:31:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/d6/46/6dcdf084a523dbe0a0be59d054734b86a981726f221f4562aed313dbcb49/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", size = 1880957, upload-time = "2025-04-23T18:31:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/ec/6b/1ec2c03837ac00886ba8160ce041ce4e325b41d06a034adbef11339ae422/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", size = 1964199, upload-time = "2025-04-23T18:31:31.025Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1d/6bf34d6adb9debd9136bd197ca72642203ce9aaaa85cfcbfcf20f9696e83/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", size = 2120296, upload-time = "2025-04-23T18:31:32.514Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/2bd0aaf5a591e974b32a9f7123f16637776c304471a0ab33cf263cf5591a/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", size = 2676109, upload-time = "2025-04-23T18:31:33.958Z" }, + { url = "https://files.pythonhosted.org/packages/f9/41/4b043778cf9c4285d59742281a769eac371b9e47e35f98ad321349cc5d61/pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", size = 2002028, upload-time = "2025-04-23T18:31:39.095Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d5/7bb781bf2748ce3d03af04d5c969fa1308880e1dca35a9bd94e1a96a922e/pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", size = 2100044, upload-time = "2025-04-23T18:31:41.034Z" }, + { url = "https://files.pythonhosted.org/packages/fe/36/def5e53e1eb0ad896785702a5bbfd25eed546cdcf4087ad285021a90ed53/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", size = 2058881, upload-time = "2025-04-23T18:31:42.757Z" }, + { url = "https://files.pythonhosted.org/packages/01/6c/57f8d70b2ee57fc3dc8b9610315949837fa8c11d86927b9bb044f8705419/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", size = 2227034, upload-time = "2025-04-23T18:31:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/27/b9/9c17f0396a82b3d5cbea4c24d742083422639e7bb1d5bf600e12cb176a13/pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", size = 2234187, upload-time = "2025-04-23T18:31:45.891Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/adf5734ffd52bf86d865093ad70b2ce543415e0e356f6cacabbc0d9ad910/pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", size = 1892628, upload-time = "2025-04-23T18:31:47.819Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5479fecb3606c1368d496a825d8411e126133c41224c1e7238be58b87d7e/pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", size = 1955866, upload-time = "2025-04-23T18:31:49.635Z" }, + { url = "https://files.pythonhosted.org/packages/0d/24/8b11e8b3e2be9dd82df4b11408a67c61bb4dc4f8e11b5b0fc888b38118b5/pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", size = 1888894, upload-time = "2025-04-23T18:31:51.609Z" }, + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688, upload-time = "2025-04-23T18:31:53.175Z" }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808, upload-time = "2025-04-23T18:31:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580, upload-time = "2025-04-23T18:31:57.393Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859, upload-time = "2025-04-23T18:31:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810, upload-time = "2025-04-23T18:32:00.78Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498, upload-time = "2025-04-23T18:32:02.418Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611, upload-time = "2025-04-23T18:32:04.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924, upload-time = "2025-04-23T18:32:06.129Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196, upload-time = "2025-04-23T18:32:08.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389, upload-time = "2025-04-23T18:32:10.242Z" }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223, upload-time = "2025-04-23T18:32:12.382Z" }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473, upload-time = "2025-04-23T18:32:14.034Z" }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269, upload-time = "2025-04-23T18:32:15.783Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921, upload-time = "2025-04-23T18:32:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162, upload-time = "2025-04-23T18:32:20.188Z" }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560, upload-time = "2025-04-23T18:32:22.354Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" }, + { url = "https://files.pythonhosted.org/packages/30/68/373d55e58b7e83ce371691f6eaa7175e3a24b956c44628eb25d7da007917/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", size = 2023982, upload-time = "2025-04-23T18:32:53.14Z" }, + { url = "https://files.pythonhosted.org/packages/a4/16/145f54ac08c96a63d8ed6442f9dec17b2773d19920b627b18d4f10a061ea/pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", size = 1858412, upload-time = "2025-04-23T18:32:55.52Z" }, + { url = "https://files.pythonhosted.org/packages/41/b1/c6dc6c3e2de4516c0bb2c46f6a373b91b5660312342a0cf5826e38ad82fa/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", size = 1892749, upload-time = "2025-04-23T18:32:57.546Z" }, + { url = "https://files.pythonhosted.org/packages/12/73/8cd57e20afba760b21b742106f9dbdfa6697f1570b189c7457a1af4cd8a0/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", size = 2067527, upload-time = "2025-04-23T18:32:59.771Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d5/0bb5d988cc019b3cba4a78f2d4b3854427fc47ee8ec8e9eaabf787da239c/pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", size = 2108225, upload-time = "2025-04-23T18:33:04.51Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c5/00c02d1571913d496aabf146106ad8239dc132485ee22efe08085084ff7c/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", size = 2069490, upload-time = "2025-04-23T18:33:06.391Z" }, + { url = "https://files.pythonhosted.org/packages/22/a8/dccc38768274d3ed3a59b5d06f59ccb845778687652daa71df0cab4040d7/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", size = 2237525, upload-time = "2025-04-23T18:33:08.44Z" }, + { url = "https://files.pythonhosted.org/packages/d4/e7/4f98c0b125dda7cf7ccd14ba936218397b44f50a56dd8c16a3091df116c3/pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", size = 2238446, upload-time = "2025-04-23T18:33:10.313Z" }, + { url = "https://files.pythonhosted.org/packages/ce/91/2ec36480fdb0b783cd9ef6795753c1dea13882f2e68e73bce76ae8c21e6a/pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", size = 2066678, upload-time = "2025-04-23T18:33:12.224Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/d4ae6487d73948d6f20dddcd94be4ea43e74349b56eba82e9bdee2d7494c/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", size = 2025200, upload-time = "2025-04-23T18:33:14.199Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/b3cb95375f05d33801024079b9392a5ab45267a63400bf1866e7ce0f0de4/pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", size = 1859123, upload-time = "2025-04-23T18:33:16.555Z" }, + { url = "https://files.pythonhosted.org/packages/05/bc/0d0b5adeda59a261cd30a1235a445bf55c7e46ae44aea28f7bd6ed46e091/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", size = 1892852, upload-time = "2025-04-23T18:33:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/d37bdebbda2e449cb3f519f6ce950927b56d62f0b84fd9cb9e372a26a3d5/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", size = 2067484, upload-time = "2025-04-23T18:33:20.475Z" }, + { url = "https://files.pythonhosted.org/packages/8c/55/1f95f0a05ce72ecb02a8a8a1c3be0579bbc29b1d5ab68f1378b7bebc5057/pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", size = 2108896, upload-time = "2025-04-23T18:33:22.501Z" }, + { url = "https://files.pythonhosted.org/packages/53/89/2b2de6c81fa131f423246a9109d7b2a375e83968ad0800d6e57d0574629b/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", size = 2069475, upload-time = "2025-04-23T18:33:24.528Z" }, + { url = "https://files.pythonhosted.org/packages/b8/e9/1f7efbe20d0b2b10f6718944b5d8ece9152390904f29a78e68d4e7961159/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", size = 2239013, upload-time = "2025-04-23T18:33:26.621Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/5309c905a93811524a49b4e031e9851a6b00ff0fb668794472ea7746b448/pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", size = 2238715, upload-time = "2025-04-23T18:33:28.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/56/8a7ca5d2cd2cda1d245d34b1c9a942920a718082ae8e54e5f3e5a58b7add/pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", size = 2066757, upload-time = "2025-04-23T18:33:30.645Z" }, +] + +[[package]] +name = "sgp4" +version = "2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/d0/fc467010d17742321f73b16a71acac88439a88f2b166641942a6566c9b2a/sgp4-2.25.tar.gz", hash = "sha256:e19edc6dcc25d69fb8fde0a267b8f0c44d7e915c7bcbeacf5d3a8b595baf0674", size = 181016, upload-time = "2025-08-04T18:02:33.765Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/86/f329af1f37f88693a7e8db92f9c0bf92a7e7dc44c272f89a2808b0582766/sgp4-2.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29fd9ad2ded9517f6ba10f91e2d993144400c6a925e2b7931198646625beafd4", size = 162967, upload-time = "2025-08-04T18:01:39.296Z" }, + { url = "https://files.pythonhosted.org/packages/87/f0/c258a86bf9f50b46295bdc32af15954719ba88a12267572f7123ffc66f0d/sgp4-2.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ad88a8ced4b78f337765e8463f7f11c5f86d9267f83fc8e3dd8982df67bff45", size = 161934, upload-time = "2025-08-04T18:01:40.99Z" }, + { url = "https://files.pythonhosted.org/packages/a6/4f/f437ca8daaba0e3c4ce67a997f30eead0489908527d6b358418ed4a3e8b1/sgp4-2.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc0c6ccb0f83e670e50dcd8a90b8a5bfe5bbf4225ce8450f807e14acc517ab21", size = 235757, upload-time = "2025-08-04T18:01:42.164Z" }, + { url = "https://files.pythonhosted.org/packages/29/19/e8f990fc80d508e7957f9b621366db05d384f5af70d05f77376be74e3007/sgp4-2.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3282ec0931e57692f3bf875342f28f41b1155cb575cbe24a30c3cd272ea46fb5", size = 232737, upload-time = "2025-08-04T18:01:43.456Z" }, + { url = "https://files.pythonhosted.org/packages/26/e3/898d7a6d31309cb1be2b2b68dd14141da300ee7257bc30774759a9c1a323/sgp4-2.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18e44f66670c61ae2372d6fecde076cb655f76d211b34b8de440cad5a273409f", size = 235207, upload-time = "2025-08-04T18:01:44.532Z" }, + { url = "https://files.pythonhosted.org/packages/87/c2/bb42e252846d29f668ff26b42adf65c842c8985c033b58d61d1c810728aa/sgp4-2.25-cp310-cp310-win32.whl", hash = "sha256:a2cc50b72b7d2b04c4012b492ec0e76f085e84de45f5e56d3baa4d3ef5f65dac", size = 161870, upload-time = "2025-08-04T18:01:45.461Z" }, + { url = "https://files.pythonhosted.org/packages/23/e8/3e44b47d3ccab7ae02a03d8107651c080da671a0c6e49cb97dd953f10809/sgp4-2.25-cp310-cp310-win_amd64.whl", hash = "sha256:2b92506eef5c07063ab7595db58373bd965f8969fb1fb5b76cbffeb39027ba93", size = 164101, upload-time = "2025-08-04T18:01:46.439Z" }, + { url = "https://files.pythonhosted.org/packages/03/1c/388f1b70a637e3bee179fae1031dcdd8ce09bd040bbbe80fcba20a2e2b86/sgp4-2.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93b22b9ae35db33664f2ddc37955a8d86c3a28f5c668d201e8c6f195a184496f", size = 162964, upload-time = "2025-08-04T18:01:47.866Z" }, + { url = "https://files.pythonhosted.org/packages/47/42/7f27ceca4730d2af31b20928b1c0f1f924cd942c2709d11fc52d9e02f48e/sgp4-2.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:33048ff064a4c0b6d8e3c2c79449a49ff45f5dabe8594622f0fb7ed17fa27c0e", size = 161937, upload-time = "2025-08-04T18:01:48.951Z" }, + { url = "https://files.pythonhosted.org/packages/d5/9f/99b1587bd3e6c17405efbd9e48603ae194c9d53938e1f8946c6c5d18c33f/sgp4-2.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac94f1d6fae120beeb40f2af587b351f9cb198837ae0fb3678e3bce44334a2a2", size = 235730, upload-time = "2025-08-04T18:01:49.905Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5a/76f56a0466c3a916403b320363a0f10e71db5a34d2b3627d6a92d2eb9d08/sgp4-2.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b164e636c4f1c64e09c6164b85985395c28c8556bc72ea56e42a889826287a0", size = 232736, upload-time = "2025-08-04T18:01:50.973Z" }, + { url = "https://files.pythonhosted.org/packages/16/f8/c1216d3c85341e30d79c9ca27b2c27dba6ed0238c56e8b04ef568ea92c50/sgp4-2.25-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfaddc20c4d6aa2e86119d13e3fd94a1d05e5bd17cb4fddb2ca5116842bc9228", size = 235203, upload-time = "2025-08-04T18:01:52.275Z" }, + { url = "https://files.pythonhosted.org/packages/52/c6/846a8039a0e8c5b653265e0248ee9836c75f420cfe99a65215a23f035595/sgp4-2.25-cp311-cp311-win32.whl", hash = "sha256:0ecd7d8833f83fe426d7926149665f4f23f4dab34b844e50876a1df88ee9aa7b", size = 161868, upload-time = "2025-08-04T18:01:53.256Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ff/a74904468464d01dd52affb4b15ee1fe5ea804e5128a3f5f5f38c954765d/sgp4-2.25-cp311-cp311-win_amd64.whl", hash = "sha256:6b023f81fb20e62f8fa0b6f506201539ca8306779ef8565422bbf000f1e5a3dc", size = 164096, upload-time = "2025-08-04T18:01:54.264Z" }, + { url = "https://files.pythonhosted.org/packages/0a/71/864524bde46a02e636cc5de47b9a4e1f1ed18c7acc3f1319cf9fe1db3c7a/sgp4-2.25-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:170ec2882cd166ff9d8dccfb8018f86d5cc033ea8a07c27a1825999c62439f05", size = 162985, upload-time = "2025-08-04T18:01:55.646Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cd/022aa419d9570d494dafd5103a71dda64c6ffc956a1c7f5b096a58a23a6a/sgp4-2.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:64c7597a60b770caac51566b1f621d1cd74df0409ef19c5e7ea3505d0dfbc677", size = 161951, upload-time = "2025-08-04T18:01:56.745Z" }, + { url = "https://files.pythonhosted.org/packages/3a/1c/76dbf2190d30a770fe8ac57474d212e005f56f47e65dd6fcecdb546d454f/sgp4-2.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e1d18b8972643dd29e758e67c062cfb68fbe2421fe3f6398f1957a9825119f6", size = 236340, upload-time = "2025-08-04T18:01:57.778Z" }, + { url = "https://files.pythonhosted.org/packages/97/a4/2fc9bf9cb75571222bd453407317e91193a3db1c559333c5e46ce7a014c9/sgp4-2.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35649388a06cbee7def24cbb789f452c31d42ed9e87bddd89935ed78f19451ed", size = 233080, upload-time = "2025-08-04T18:01:58.812Z" }, + { url = "https://files.pythonhosted.org/packages/fc/40/50ecdc518edd3a85ad74bda7a2196b53d5901256e3d7ab34225c96e8edc8/sgp4-2.25-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:911460477f1c52dcda2b3eb20538435b89b0a43668bcb5edd1e7700b7a1a0225", size = 235729, upload-time = "2025-08-04T18:01:59.83Z" }, + { url = "https://files.pythonhosted.org/packages/1b/dd/c1ee8571828debfd3e0f2297379a2a2af75024062c70cf76bdc121e77623/sgp4-2.25-cp312-cp312-win32.whl", hash = "sha256:128edd3d6061e833600d93e77d4c08d1a5002293997e368256b0b777ea525dda", size = 161899, upload-time = "2025-08-04T18:02:00.882Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f8/7dae15af520dfe5def1f8620c2817203cbbf1a1bf154b2079add1200acd3/sgp4-2.25-cp312-cp312-win_amd64.whl", hash = "sha256:979eb60e74aff5dc318cfe1a6c817db884486bdfc8496d2c5bc07b05fe833280", size = 164137, upload-time = "2025-08-04T18:02:01.817Z" }, + { url = "https://files.pythonhosted.org/packages/02/0f/daf4a70829be7c1550b914c98b3abbd15404d00899835432ae8d4a9be502/sgp4-2.25-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c4d4eab0f2c94aad3a0ab0bedd59f2137484af5480a3b40df8e4ab5a1fbc6b86", size = 162974, upload-time = "2025-08-04T18:02:02.816Z" }, + { url = "https://files.pythonhosted.org/packages/27/88/af20e342590c3ede18cc8dc6a1e1da708f576e1a97dcb69e2870e739ae21/sgp4-2.25-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2822ca25f3724694bfced16cad8b3018678bee47fa3baf4eea20876d0e35ad33", size = 161957, upload-time = "2025-08-04T18:02:03.835Z" }, + { url = "https://files.pythonhosted.org/packages/6e/14/81f0df0cc39bdc95336a6f5834c84a6e5f79b5e728918cb9dadff3278017/sgp4-2.25-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7beca36492eb6d20ef15eeedd9520b8af4fa0cbaaae46a9269d5a2e7c8e56e46", size = 236195, upload-time = "2025-08-04T18:02:05.121Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a7/3740791f656d9b7ad78da7c0d9f6f842a18642fead2d26b2d69fb701892e/sgp4-2.25-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e9dfd18cacf6bfb1faad29c89a6cec98a642558f805851080dea9c394520db2", size = 232992, upload-time = "2025-08-04T18:02:06.086Z" }, + { url = "https://files.pythonhosted.org/packages/62/45/0e35398ef8d4b07ecfa9f7f680e183b2b6af9215a56af34f9e621c29b495/sgp4-2.25-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5789b7add136362684dfcbf0862919f8c3018f74ab11a05a9964edd5fdd4d2a7", size = 235584, upload-time = "2025-08-04T18:02:07.152Z" }, + { url = "https://files.pythonhosted.org/packages/47/b7/1b680b5514586b3860500109ef37fd1761f21e6787f20a2597baa44d91a0/sgp4-2.25-cp313-cp313-win32.whl", hash = "sha256:94219b486def29aa1246f42de8bea05ccb8e98a5458dd08ce42b9811c79ca814", size = 161896, upload-time = "2025-08-04T18:02:08.062Z" }, + { url = "https://files.pythonhosted.org/packages/67/c1/a22be2e7886db40d1512969f3b8cc3ce989167e69ea8f308f26afd1dfd31/sgp4-2.25-cp313-cp313-win_amd64.whl", hash = "sha256:dec2f6c842d9bf40c67d5764bd752980844f91f338020d2af7f85847364d0ff7", size = 164142, upload-time = "2025-08-04T18:02:09Z" }, +] + +[[package]] +name = "skyfield" +version = "1.53" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "jplephem" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sgp4" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/cb/0c9f9685f6ff3b9c268b1d995c584f0d09de160f46fbd6c1df0631565bfd/skyfield-1.53.tar.gz", hash = "sha256:24099855f3ba3906663ac1c10e650041e747680b986e807400eddedc0be4a8b4", size = 343126, upload-time = "2025-04-07T23:32:11.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/f0/c031c7a0e14b75e68c69d90ae8966d6d2383f6e33379914eea0f41bf9518/skyfield-1.53-py3-none-any.whl", hash = "sha256:f2028bba5f3617ef34afab1cabac251601f72a8cd70298ca5def72c4beadce00", size = 366966, upload-time = "2025-04-07T23:32:09.76Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "starlette" +version = "0.47.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/b9/cc3017f9a9c9b6e27c5106cc10cc7904653c3eec0729793aec10479dd669/starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9", size = 2584144, upload-time = "2025-08-24T13:36:42.122Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/fd/901cfa59aaa5b30a99e16876f11abe38b59a1a2c51ffb3d7142bb6089069/starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51", size = 72991, upload-time = "2025-08-24T13:36:40.887Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726, upload-time = "2025-05-21T18:55:23.885Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, +] + +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, +]