initial commit
This commit is contained in:
BIN
__pycache__/ai_service.cpython-311.pyc
Normal file
BIN
__pycache__/ai_service.cpython-311.pyc
Normal file
Binary file not shown.
549
ai_service.py
Normal file
549
ai_service.py
Normal file
@@ -0,0 +1,549 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
import json
|
||||
import requests
|
||||
import re
|
||||
import traceback
|
||||
import duckdb
|
||||
import math
|
||||
import os
|
||||
|
||||
from typing import Optional, Any, TypedDict, List, Dict, cast
|
||||
from datetime import datetime, date
|
||||
from decimal import Decimal
|
||||
import time
|
||||
|
||||
# ---------------------------------------------------
|
||||
# SCHEMAS (Pydantic for strict enforcement)
|
||||
# ---------------------------------------------------
|
||||
|
||||
class ChartConfig(BaseModel):
|
||||
type: str # e.g., "bar", "pie", "line", "area", "table", "kpi"
|
||||
title: str
|
||||
x_axis: Optional[str] = None
|
||||
y_axis: Optional[str] = None
|
||||
data: List[Dict[str, Any]]
|
||||
|
||||
class AnalyticsResponse(BaseModel):
|
||||
analysis: str
|
||||
charts: List[ChartConfig]
|
||||
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from langgraph.graph import StateGraph, END
|
||||
|
||||
# ---------------------------------------------------
|
||||
# CONFIG
|
||||
# ---------------------------------------------------
|
||||
|
||||
OLLAMA_URL = "http://localhost:11434/api/chat"
|
||||
MODEL = "qwen2.5:3b"
|
||||
# DuckDB should connect to a local persistent file
|
||||
DUCKDB_PATH = "analytics.duckdb"
|
||||
DEBUG_LOG = "debug.log"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# FASTAPI
|
||||
# ---------------------------------------------------
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# ---------------------------------------------------
|
||||
# DEBUG LOGGER
|
||||
# ---------------------------------------------------
|
||||
|
||||
def log_debug(msg: str):
|
||||
with open(DEBUG_LOG, "a", encoding="utf-8") as f:
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
f.write(f"[{timestamp}] {msg}\n")
|
||||
|
||||
def log_node_duration(label: str, start_time: float, extra: str = ""):
|
||||
duration = time.perf_counter() - start_time
|
||||
print(f"[{label}] {duration:.2f}s {extra}")
|
||||
|
||||
# ---------------------------------------------------
|
||||
# DUCKDB INIT
|
||||
# ---------------------------------------------------
|
||||
|
||||
def init_duckdb(conn):
|
||||
try:
|
||||
log_debug("Initializing DuckDB with live S3 data")
|
||||
|
||||
# Load httpfs extension for S3 access
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
|
||||
# S3 configuration matching the environment
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
# Create a view from the S3 parquet files
|
||||
s3_path = 's3://nearle/parquet/deliveries/*.parquet'
|
||||
conn.execute(f"""
|
||||
CREATE OR REPLACE VIEW deliveries AS
|
||||
SELECT * FROM read_parquet('{s3_path}', union_by_name = true)
|
||||
""")
|
||||
|
||||
log_debug(f"DuckDB ready with view 'deliveries' from {s3_path}")
|
||||
except Exception as e:
|
||||
log_debug(f"DuckDB init error: {e}")
|
||||
pass
|
||||
|
||||
# ---------------------------------------------------
|
||||
# REQUEST MODEL
|
||||
# ---------------------------------------------------
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
message: str
|
||||
data: Optional[Any] = None
|
||||
|
||||
# ---------------------------------------------------
|
||||
# LANGGRAPH STATE
|
||||
# ---------------------------------------------------
|
||||
|
||||
class WorkflowState(TypedDict):
|
||||
question: str
|
||||
query_plan: Dict[str, Any]
|
||||
sql_query: str
|
||||
data: List[Dict[str, Any]]
|
||||
analysis: str
|
||||
insights: List[str]
|
||||
data_quality: Dict[str, Any]
|
||||
final_response: Dict[str, Any]
|
||||
error: Optional[str]
|
||||
iteration_count: int
|
||||
|
||||
# ---------------------------------------------------
|
||||
# OLLAMA CALL
|
||||
# ---------------------------------------------------
|
||||
|
||||
def call_ollama(system_prompt: str, user_prompt: str) -> str:
|
||||
for attempt in range(3):
|
||||
try:
|
||||
payload = {
|
||||
"model": MODEL,
|
||||
"messages": [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
],
|
||||
"stream": False,
|
||||
"options": {
|
||||
"temperature": 0,
|
||||
"num_predict": 200, # 🔥 increased to prevent cutoff
|
||||
}
|
||||
}
|
||||
|
||||
r = requests.post(OLLAMA_URL, json=payload, timeout=20)
|
||||
r.raise_for_status()
|
||||
|
||||
content = r.json().get("message", {}).get("content", "")
|
||||
if content and content.strip() != "":
|
||||
return content
|
||||
log_debug(f"Ollama empty response on attempt {attempt + 1}")
|
||||
except Exception as e:
|
||||
log_debug(f"Ollama call error: {e} on attempt {attempt + 1}")
|
||||
|
||||
time.sleep(1)
|
||||
return ""
|
||||
|
||||
# ---------------------------------------------------
|
||||
# UTILITIES
|
||||
# ---------------------------------------------------
|
||||
|
||||
def safe_parse_json(text: str) -> Dict[str, Any]:
|
||||
"""Robustly parse JSON from LLM response."""
|
||||
try:
|
||||
return json.loads(text.strip())
|
||||
except json.JSONDecodeError:
|
||||
match = re.search(r"```json\s*(.*?)\s*```", text, re.S)
|
||||
if match:
|
||||
try:
|
||||
return json.loads(match.group(1).strip())
|
||||
except: pass
|
||||
|
||||
start = text.find("{")
|
||||
end = text.rfind("}")
|
||||
if start != -1 and end != -1:
|
||||
try:
|
||||
return json.loads(text[start:end+1])
|
||||
except: pass
|
||||
raise ValueError("Could not find valid JSON in LLM response")
|
||||
|
||||
# ---------------------------------------------------
|
||||
# NODES
|
||||
# ---------------------------------------------------
|
||||
|
||||
def query_planner_node(state: WorkflowState) -> WorkflowState:
|
||||
log_debug(f"NODE: Query Planner - {state['question']}")
|
||||
print(f"\n🔍 NODE: PLANNER (Iteration: {state.get('iteration_count', 0)})")
|
||||
start = time.perf_counter()
|
||||
|
||||
system = """You are an AI SQL Planner.
|
||||
|
||||
Return ONLY valid JSON. No explanation.
|
||||
|
||||
---
|
||||
|
||||
## DATABASE
|
||||
|
||||
Table: deliveries
|
||||
|
||||
Columns:
|
||||
|
||||
* orderid
|
||||
* tenantname
|
||||
* deliveryamt (can be TEXT or NUMERIC)
|
||||
* deliverydate
|
||||
|
||||
---
|
||||
|
||||
## RULES
|
||||
|
||||
1. Revenue = SUM(CAST(deliveryamt AS NUMERIC))
|
||||
|
||||
2. Never use:
|
||||
|
||||
* orderamount
|
||||
* unsafe filters
|
||||
* TRIM unless needed
|
||||
|
||||
3. SQL must NEVER fail:
|
||||
|
||||
* Always CAST for SUM
|
||||
* Keep query simple
|
||||
|
||||
4. If previous query failed:
|
||||
|
||||
* Use ERROR below to FIX SQL
|
||||
* NEVER repeat same query
|
||||
|
||||
ERROR:
|
||||
{error}
|
||||
|
||||
---
|
||||
|
||||
## OUTPUT FORMAT
|
||||
|
||||
You MUST return a JSON object containing EXACTLY these 6 keys:
|
||||
|
||||
{
|
||||
"intent": "kpi | trend | comparison | table",
|
||||
"sql": "valid SQL query",
|
||||
"chart_type": "kpi | bar | line | pie | table",
|
||||
"x_axis": "column or null",
|
||||
"y_axis": "column or null",
|
||||
"explanation": "short explanation"
|
||||
}"""
|
||||
|
||||
error_msg = state.get("error", "")
|
||||
system = system.replace("{error}", str(error_msg))
|
||||
|
||||
error_msg = state.get("error")
|
||||
if error_msg and state.get("iteration_count", 0) > 0:
|
||||
log_debug(f"Retrying SQL generation due to error: {error_msg}")
|
||||
user_prompt = f"User Query:\n{state['question']}\n\nWarning: Your previous SQL query failed with this error in DuckDB:\n{error_msg}\n\nPlease fix the SQL query and return the correct JSON plan."
|
||||
else:
|
||||
user_prompt = f"User Query:\n{state['question']}"
|
||||
|
||||
result = call_ollama(system, user_prompt)
|
||||
|
||||
print("\n--- RAW LLM RESPONSE ---")
|
||||
print(result)
|
||||
print("------------------------\n")
|
||||
print("RESPONSE LENGTH:", len(result))
|
||||
|
||||
if not result or result.strip() == "":
|
||||
raise Exception("LLM returned empty response")
|
||||
|
||||
try:
|
||||
plan = safe_parse_json(result)
|
||||
|
||||
# Strict validation
|
||||
for key in ["intent", "sql", "chart_type", "y_axis"]:
|
||||
if key not in plan:
|
||||
raise ValueError(f"Missing required key: {key}")
|
||||
|
||||
if plan.get("intent") == "ranking" and "GROUP BY" not in str(plan.get("sql", "")).upper():
|
||||
raise ValueError("Ranking query SQL must contain a GROUP BY clause")
|
||||
|
||||
print("\n--- PARSED PLAN ---")
|
||||
print(json.dumps(plan, indent=2))
|
||||
print("-------------------\n")
|
||||
|
||||
print("\n--- FINAL SQL USED ---")
|
||||
print(plan["sql"])
|
||||
print("----------------------\n")
|
||||
|
||||
except Exception as e:
|
||||
log_debug(f"Planner JSON parsing error: {str(e)} for output: {result}")
|
||||
print(f"\n❌ ERROR: Invalid LLM response. Original error: {str(e)}")
|
||||
raise Exception("Invalid LLM response")
|
||||
|
||||
log_node_duration("⏱️ Planner", start)
|
||||
return {**state, "query_plan": plan, "sql_query": plan.get("sql", ""), "error": None}
|
||||
|
||||
def execute_sql_node(state: WorkflowState) -> WorkflowState:
|
||||
log_debug(f"NODE: Execute SQL - {state['sql_query']}")
|
||||
print(f"\n🗄️ NODE: SQL EXECUTION")
|
||||
start = time.perf_counter()
|
||||
|
||||
try:
|
||||
conn = duckdb.connect(DUCKDB_PATH)
|
||||
init_duckdb(conn)
|
||||
|
||||
df = conn.execute(state["sql_query"]).df()
|
||||
sql_results = df.to_dict("records")
|
||||
conn.close()
|
||||
|
||||
# Sanitize data for JSON response
|
||||
clean_data = []
|
||||
for r in sql_results:
|
||||
row_clean = {}
|
||||
for k, v in r.items():
|
||||
if isinstance(v, Decimal): v = float(v)
|
||||
if isinstance(v, float) and (math.isnan(v) or math.isinf(v)): v = 0.0
|
||||
if isinstance(v, (datetime, date)):
|
||||
# Format as clean YYYY-MM-DD to save chart axis space
|
||||
v = v.strftime("%Y-%m-%d")
|
||||
|
||||
# Replace bad string primitives from DB with recognizable null equivalent
|
||||
if v is None or (isinstance(v, str) and v.strip() == ""):
|
||||
# Ignore for numeric indicators
|
||||
if not ("orders" in k or "revenue" in k or "avg_order_value" in k or "charges" in k):
|
||||
v = "Unknown"
|
||||
row_clean[k] = v
|
||||
clean_data.append(row_clean)
|
||||
|
||||
log_node_duration("⏱️ QUERY EXECUTION", start, f"({len(clean_data)} rows)")
|
||||
return {**state, "data": clean_data, "error": None, "iteration_count": int(state.get("iteration_count", 0) or 0) + 1}
|
||||
except Exception as e:
|
||||
log_debug(f"SQL Error: {e}")
|
||||
log_node_duration("⏱️ QUERY EXECUTION", start, "(ERROR)")
|
||||
return {**state, "error": str(e), "iteration_count": int(state.get("iteration_count", 0) or 0) + 1}
|
||||
|
||||
def validate_data_node(state: WorkflowState) -> WorkflowState:
|
||||
log_debug("NODE: Data Quality Analysis")
|
||||
warnings = []
|
||||
|
||||
if not state.get("data"):
|
||||
warnings.append("No data returned for your query.")
|
||||
return {**state, "data_quality": {"warnings": warnings}}
|
||||
|
||||
all_zeros = True
|
||||
for row in state["data"]:
|
||||
for k, v in row.items():
|
||||
if isinstance(v, (int, float)) and v > 0:
|
||||
all_zeros = False
|
||||
break
|
||||
if not all_zeros: break
|
||||
|
||||
if all_zeros:
|
||||
warnings.append("All metrics returned zero values.")
|
||||
|
||||
return {**state, "data_quality": {"warnings": warnings}}
|
||||
|
||||
def analyze_results_node(state: WorkflowState) -> WorkflowState:
|
||||
log_debug("NODE: Results Analysis")
|
||||
data = state.get("data", [])
|
||||
if not data or state.get("error"):
|
||||
return {**state, "analysis": "No data found to analyze."}
|
||||
|
||||
row_count = len(data)
|
||||
plan = state.get("query_plan", {})
|
||||
metric = plan.get("y_axis")
|
||||
dimension = plan.get("x_axis")
|
||||
|
||||
if dimension and metric and row_count > 0:
|
||||
top = data[0]
|
||||
analysis = f"Analysis of {row_count} records shows that {dimension} '{(top.get(dimension, 'Unknown'))}' is the top entry with {metric} of {top.get(metric, 0)}."
|
||||
elif metric and row_count > 0:
|
||||
top = data[0]
|
||||
analysis = f"Analysis completed. The total {metric} is {top.get(metric, 0)}."
|
||||
else:
|
||||
analysis = f"Retrieved {row_count} records for the requested analysis."
|
||||
|
||||
return {**state, "analysis": analysis}
|
||||
|
||||
def insights_node(state: WorkflowState) -> WorkflowState:
|
||||
log_debug("NODE: Insights Generation")
|
||||
print(f"\n💡 NODE: INSIGHTS")
|
||||
start = time.perf_counter()
|
||||
data = cast(list, state.get("data", []))
|
||||
if not data or state.get("error"):
|
||||
return {**state, "insights": []}
|
||||
|
||||
insights = []
|
||||
plan = state.get("query_plan", {})
|
||||
metric = plan.get("y_axis")
|
||||
dimension = plan.get("x_axis")
|
||||
|
||||
if metric and dimension and len(data) > 0:
|
||||
top = data[0]
|
||||
insights.append(f"Top performer is {top.get(dimension, 'Unknown')} with {top.get(metric, 0)} {metric}.")
|
||||
if len(data) > 1:
|
||||
bottom = data[-1]
|
||||
insights.append(f"Lowest performer is {bottom.get(dimension, 'Unknown')} with {bottom.get(metric, 0)} {metric}.")
|
||||
try:
|
||||
total = sum(float(row.get(metric, 0)) for row in data if row.get(metric) is not None)
|
||||
avg_val = total / len(data)
|
||||
insights.append(f"The average {metric} across these records is {avg_val:.2f}.")
|
||||
except:
|
||||
insights.append("Data volume supports high-confidence trend analysis.")
|
||||
elif metric and len(data) > 0:
|
||||
top = data[0]
|
||||
insights.append(f"The overall {metric} is {top.get(metric, 0)}.")
|
||||
else:
|
||||
insights = ["Data supports standard trend analysis."]
|
||||
|
||||
if state.get("data_quality") and state["data_quality"].get("warnings"):
|
||||
for w in state["data_quality"]["warnings"]:
|
||||
insights.append(f"⚠ {w}")
|
||||
|
||||
log_node_duration("⏱️ Insights", start)
|
||||
return {**state, "insights": insights}
|
||||
|
||||
def select_visualization_node(state: WorkflowState) -> WorkflowState:
|
||||
log_debug("NODE: Visualization Selection")
|
||||
print(f"\n📊 NODE: VISUALIZATION")
|
||||
start = time.perf_counter()
|
||||
|
||||
data = state.get("data", [])
|
||||
if not data or state.get("error"):
|
||||
return {**state, "final_response": {
|
||||
"analysis": state.get("error", "No data available to plot a chart."),
|
||||
"charts": []
|
||||
}}
|
||||
|
||||
plan = state.get("query_plan", {})
|
||||
intent = plan.get("intent", "kpi")
|
||||
|
||||
chart_type = plan.get("chart_type", "kpi")
|
||||
x_axis = plan.get("x_axis")
|
||||
y_axis = plan.get("y_axis")
|
||||
|
||||
if intent and "table" in intent.lower():
|
||||
chart_type = "table"
|
||||
|
||||
if intent and "list" in intent.lower():
|
||||
chart_type = "table"
|
||||
|
||||
if chart_type == "kpi" and len(data) == 1:
|
||||
data = [{**data[0], "summary": "Current Performance"}]
|
||||
if not x_axis:
|
||||
x_axis = "summary"
|
||||
|
||||
if chart_type == "kpi" and len(data) > 1:
|
||||
chart_type = "bar"
|
||||
|
||||
chart = {
|
||||
"type": chart_type,
|
||||
"title": f"{str(intent).replace('_', ' ').title()} Analysis" if intent else "Data Visualization",
|
||||
"x_axis": x_axis,
|
||||
"y_axis": y_axis,
|
||||
"data": data
|
||||
}
|
||||
|
||||
ai_decision = {
|
||||
"intent": intent,
|
||||
"chart_type": chart_type,
|
||||
"x_axis": x_axis,
|
||||
"y_axis": y_axis,
|
||||
"reason": "Directly mapped from LLM Engine structured plan.",
|
||||
}
|
||||
|
||||
final_response = {
|
||||
"analysis": state["analysis"],
|
||||
"ai_decision": ai_decision,
|
||||
"insights": state.get("insights", []),
|
||||
"generated_sql": state.get("sql_query", ""),
|
||||
"query_plan": plan,
|
||||
"data_quality": state.get("data_quality", {"warnings": []}),
|
||||
"charts": [chart],
|
||||
"explanation": state["analysis"]
|
||||
}
|
||||
|
||||
log_node_duration("⏱️ Visualization", start)
|
||||
return {**state, "final_response": final_response}
|
||||
|
||||
def should_retry(state: WorkflowState):
|
||||
if state.get("error") and state.get("iteration_count", 0) < 2:
|
||||
return "retry"
|
||||
return "continue"
|
||||
|
||||
# ---------------------------------------------------
|
||||
# LANGGRAPH WORKFLOW
|
||||
# ---------------------------------------------------
|
||||
|
||||
workflow = StateGraph(WorkflowState)
|
||||
|
||||
workflow.add_node("planner", query_planner_node)
|
||||
workflow.add_node("exec", execute_sql_node)
|
||||
workflow.add_node("validate", validate_data_node)
|
||||
workflow.add_node("analysis", analyze_results_node)
|
||||
workflow.add_node("insights", insights_node)
|
||||
workflow.add_node("viz", select_visualization_node)
|
||||
|
||||
workflow.set_entry_point("planner")
|
||||
|
||||
workflow.add_edge("planner", "exec")
|
||||
|
||||
# Conditional edge for retry
|
||||
workflow.add_conditional_edges(
|
||||
"exec",
|
||||
should_retry,
|
||||
{
|
||||
"retry": "planner", # Re-run planner if error
|
||||
"continue": "validate"
|
||||
}
|
||||
)
|
||||
|
||||
workflow.add_edge("validate", "analysis")
|
||||
workflow.add_edge("analysis", "insights")
|
||||
workflow.add_edge("insights", "viz")
|
||||
workflow.add_edge("viz", END)
|
||||
|
||||
graph = workflow.compile()
|
||||
|
||||
# ---------------------------------------------------
|
||||
# API
|
||||
# ---------------------------------------------------
|
||||
|
||||
@app.post("/chat")
|
||||
def chat(req: ChatRequest):
|
||||
print(f"\n🚀 NEW REQUEST: {req.message}")
|
||||
total_start = time.perf_counter()
|
||||
try:
|
||||
initial_state = {
|
||||
"question": req.message,
|
||||
"query_plan": {},
|
||||
"sql_query": "",
|
||||
"data": [],
|
||||
"analysis": "",
|
||||
"insights": [],
|
||||
"data_quality": {"warnings": []},
|
||||
"final_response": {},
|
||||
"error": None,
|
||||
"iteration_count": 0
|
||||
}
|
||||
result = graph.invoke(initial_state)
|
||||
total_duration = time.perf_counter() - total_start
|
||||
print(f"\n[✅ TOTAL TIME] {total_duration:.2f}s")
|
||||
return result["final_response"]
|
||||
|
||||
except Exception as e:
|
||||
log_debug(f"Graph overall error: {e}")
|
||||
return {
|
||||
"analysis": f"Error: {str(e)}",
|
||||
"charts": []
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
BIN
analytics.duckdb
Normal file
BIN
analytics.duckdb
Normal file
Binary file not shown.
27
check_internal_db.py
Normal file
27
check_internal_db.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import duckdb
|
||||
|
||||
def check_db():
|
||||
try:
|
||||
conn = duckdb.connect('analytics.duckdb')
|
||||
print("Checking tables/views in analytics.duckdb:")
|
||||
res = conn.execute("SELECT table_name, table_type FROM information_schema.tables").fetchall()
|
||||
for r in res:
|
||||
print(f"Name: {r[0]}, Type: {r[1]}")
|
||||
|
||||
print("\nChecking first 5 columns of deliveries:")
|
||||
try:
|
||||
print(conn.execute("DESCRIBE deliveries").df().head(5))
|
||||
except:
|
||||
print("Could not describe deliveries (missing view?)")
|
||||
|
||||
print("\nSampling 5 records from deliveries:")
|
||||
try:
|
||||
print(conn.execute("SELECT * FROM deliveries LIMIT 5").df())
|
||||
except:
|
||||
print("Could not select from deliveries")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_db()
|
||||
14
check_live_schema.py
Normal file
14
check_live_schema.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import duckdb
|
||||
|
||||
def check_schema():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
df = conn.execute("SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet') LIMIT 0").df()
|
||||
print("Columns:", df.columns.tolist())
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_schema()
|
||||
35
check_revenue.py
Normal file
35
check_revenue.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import duckdb
|
||||
import pandas as pd
|
||||
|
||||
def check_revenue():
|
||||
conn = duckdb.connect('analytics.duckdb')
|
||||
try:
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
# Check top riders by revenue
|
||||
print("--- TOP 10 RIDERS BY ORDERAMOUNT ---")
|
||||
sql = """
|
||||
SELECT
|
||||
ridername,
|
||||
SUM(orderamount) as total_order_value,
|
||||
COUNT(*) as total_deliveries,
|
||||
SUM(deliveryamt) as total_delivery_fees
|
||||
FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet', union_by_name = true)
|
||||
WHERE ridername IS NOT NULL AND ridername != ''
|
||||
GROUP BY 1
|
||||
ORDER BY 2 DESC
|
||||
LIMIT 10
|
||||
"""
|
||||
df = conn.execute(sql).df()
|
||||
print(df)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
check_revenue()
|
||||
43
generate_sample_data.py
Normal file
43
generate_sample_data.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import duckdb
|
||||
import pandas as pd
|
||||
from datetime import datetime, timedelta
|
||||
import random
|
||||
|
||||
def generate_data():
|
||||
riders = ["Rider Alpha", "Rider Beta", "Rider Gamma", "Rider Delta"]
|
||||
tenants = ["Tenant A", "Tenant B", "Tenant C"]
|
||||
statuses = ["completed", "pending", "cancelled"]
|
||||
|
||||
data = []
|
||||
for i in range(50):
|
||||
rider = random.choice(riders)
|
||||
tenant = random.choice(tenants)
|
||||
order_amt = round(random.uniform(20.0, 100.0), 2)
|
||||
delivery_amt = round(order_amt * 0.1, 2)
|
||||
delivery_charges = round(random.uniform(2.0, 5.0), 2)
|
||||
status = random.choice(statuses)
|
||||
|
||||
pickup = datetime.now() - timedelta(minutes=random.randint(10, 1000))
|
||||
# ridertime in minutes
|
||||
rider_time = random.randint(15, 45)
|
||||
delivery = pickup + timedelta(minutes=rider_time)
|
||||
|
||||
data.append({
|
||||
"ridername": rider,
|
||||
"tenantname": tenant,
|
||||
"orderamount": order_amt,
|
||||
"deliveryamt": delivery_amt,
|
||||
"deliverycharges": delivery_charges,
|
||||
"orderstatus": status,
|
||||
"pickuptime": pickup.isoformat(),
|
||||
"deliverytime": delivery.isoformat(),
|
||||
"ridertime": rider_time # explicitly adding this column for the user's example
|
||||
})
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
conn = duckdb.connect()
|
||||
conn.execute("COPY df TO 'c:/bitbucket/backup/analytics/ai-service/data/deliveries.parquet' (FORMAT PARQUET)")
|
||||
print("Generated c:/bitbucket/backup/analytics/ai-service/data/deliveries.parquet")
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_data()
|
||||
15
get_cols_line.py
Normal file
15
get_cols_line.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import duckdb
|
||||
|
||||
def get_full_schema():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
rows = conn.execute("DESCRIBE SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet')").fetchall()
|
||||
cols = [row[0] for row in rows]
|
||||
print(", ".join(cols))
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_full_schema()
|
||||
16
get_important_cols.py
Normal file
16
get_important_cols.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import duckdb
|
||||
|
||||
def get_special_cols():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
rows = conn.execute("DESCRIBE SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet')").fetchall()
|
||||
cols = [row[0] for row in rows]
|
||||
filtered = [c for c in cols if "name" in c.lower() or "amt" in c.lower() or "amount" in c.lower() or "rider" in c.lower() or "tenant" in c.lower() or "time" in c.lower() or "status" in c.lower()]
|
||||
print("Filtered Columns:", filtered)
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_special_cols()
|
||||
14
get_keys.py
Normal file
14
get_keys.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import duckdb
|
||||
|
||||
def get_keys():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
df = conn.execute("SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet') LIMIT 1").df()
|
||||
print(df.columns.tolist())
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_keys()
|
||||
15
get_live_schema.py
Normal file
15
get_live_schema.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import duckdb
|
||||
|
||||
def get_full_schema():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
rows = conn.execute("DESCRIBE SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet')").fetchall()
|
||||
for row in rows:
|
||||
print(f"{row[0]}: {row[1]}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_full_schema()
|
||||
15
get_schema_full.py
Normal file
15
get_schema_full.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import duckdb
|
||||
|
||||
def get_all_cols_types():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
rows = conn.execute("DESCRIBE SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet')").fetchall()
|
||||
for row in rows:
|
||||
print(f"{row[0]} ({row[1]})")
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_all_cols_types()
|
||||
15
get_varchars.py
Normal file
15
get_varchars.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import duckdb
|
||||
|
||||
def get_varchar_cols():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
rows = conn.execute("DESCRIBE SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet')").fetchall()
|
||||
varchars = [row[0] for row in rows if row[1] == 'VARCHAR']
|
||||
print("VARCHAR Columns:", varchars)
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_varchar_cols()
|
||||
24
list_s3.py
Normal file
24
list_s3.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import duckdb
|
||||
|
||||
def list_s3_files():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
# This might not work directly for listing buckets, but let's try reading schema of other potential paths
|
||||
paths = [
|
||||
"s3://nearle/parquet/tenants/*.parquet",
|
||||
"s3://nearle/parquet/riders/*.parquet",
|
||||
"s3://nearle/parquet/orders/*.parquet"
|
||||
]
|
||||
for p in paths:
|
||||
try:
|
||||
res = conn.execute(f"SELECT count(*) FROM read_parquet('{p}')").fetchall()
|
||||
print(f"{p}: {res[0][0]}")
|
||||
except:
|
||||
print(f"{p}: Not found")
|
||||
|
||||
if __name__ == "__main__":
|
||||
list_s3_files()
|
||||
18
live_schema.txt
Normal file
18
live_schema.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
deliveryid (INTEGER)
|
||||
orderheaderid (INTEGER)
|
||||
configid (INTEGER)
|
||||
applocationid (INTEGER)
|
||||
tenantid (INTEGER)
|
||||
locationid (INTEGER)
|
||||
partnerid (INTEGER)
|
||||
moduleid (INTEGER)
|
||||
userid (INTEGER)
|
||||
categoryid (INTEGER)
|
||||
subcategoryid (INTEGER)
|
||||
orderid (VARCHAR)
|
||||
deliverydate (TIMESTAMP WITH TIME ZONE)
|
||||
orderstatus (VARCHAR)
|
||||
itemcount (INTEGER)
|
||||
orderamount (DOUBLE)
|
||||
created (TIMESTAMP WITH TIME ZONE)
|
||||
updated (TIMESTAMP WITH TIME ZONE)
|
||||
43
peek_for_insights.py
Normal file
43
peek_for_insights.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import duckdb
|
||||
|
||||
def peek_data():
|
||||
try:
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
# Create view
|
||||
s3_path = 's3://nearle/parquet/deliveries/*.parquet'
|
||||
conn.execute(f"CREATE VIEW deliveries AS SELECT * FROM read_parquet('{s3_path}', union_by_name = true)")
|
||||
|
||||
# Test Query 1
|
||||
print("Querying Avg Delivery Time...")
|
||||
df1 = conn.execute("""
|
||||
SELECT ridername, ROUND(AVG(ridertime), 2) AS avg_delivery_time
|
||||
FROM deliveries
|
||||
WHERE ridertime > 0 AND userid IS NOT NULL
|
||||
GROUP BY ridername
|
||||
ORDER BY avg_delivery_time ASC
|
||||
LIMIT 5
|
||||
""").df()
|
||||
print(df1)
|
||||
|
||||
# Test Query 2
|
||||
print("\nQuerying Total Revenue...")
|
||||
df2 = conn.execute("""
|
||||
SELECT ridername, ROUND(SUM(deliveryamt), 2) AS total_revenue
|
||||
FROM deliveries
|
||||
WHERE ridertime > 0 AND userid IS NOT NULL
|
||||
GROUP BY ridername
|
||||
ORDER BY total_revenue DESC
|
||||
LIMIT 5
|
||||
""").df()
|
||||
print(df2)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
peek_data()
|
||||
15
peek_live.py
Normal file
15
peek_live.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import duckdb
|
||||
import json
|
||||
|
||||
def peek_data():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
row = conn.execute("SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet') LIMIT 1").df().to_dict('records')[0]
|
||||
print(json.dumps(row, indent=2, default=str))
|
||||
|
||||
if __name__ == "__main__":
|
||||
peek_data()
|
||||
5
pyrightconfig.json
Normal file
5
pyrightconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"venvPath": ".",
|
||||
"venv": "venv",
|
||||
"pythonPath": "./venv/Scripts/python.exe"
|
||||
}
|
||||
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
fastapi
|
||||
uvicorn
|
||||
pydantic
|
||||
requests
|
||||
duckdb
|
||||
langgraph
|
||||
pandas
|
||||
16
save_schema.py
Normal file
16
save_schema.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import duckdb
|
||||
|
||||
def save_schema():
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
rows = conn.execute("DESCRIBE SELECT * FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet')").fetchall()
|
||||
with open('live_schema.txt', 'w') as f:
|
||||
for row in rows:
|
||||
f.write(f"{row[0]} ({row[1]})\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
save_schema()
|
||||
12
test_api.py
Normal file
12
test_api.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
url = "http://localhost:8000/chat"
|
||||
payload = {"message": "Who is the top revenue tenant?"}
|
||||
try:
|
||||
response = requests.post(url, json=payload, timeout=120)
|
||||
data = response.json()
|
||||
print(f"Analysis: {data.get('analysis')}")
|
||||
print(f"Generated SQL: {data.get('generated_sql')}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
16
test_repro.py
Normal file
16
test_repro.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import re
|
||||
import json
|
||||
|
||||
def safe_parse_json(text: str):
|
||||
start = text.find("{")
|
||||
end = text.rfind("}")
|
||||
print(f"text type: {type(text)}")
|
||||
print(f"start: {start}, end: {end}")
|
||||
if start != -1 and end != -1:
|
||||
json_str = text[int(start) : int(end) + 1]
|
||||
print(f"json_str: {json_str}")
|
||||
return json.loads(json_str)
|
||||
return None
|
||||
|
||||
test_text = "Filler text {\"key\": \"value\"} more filler"
|
||||
print(safe_parse_json(test_text))
|
||||
33
test_rider_sql.py
Normal file
33
test_rider_sql.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import duckdb
|
||||
|
||||
def run_test():
|
||||
try:
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
print("Querying riders with ridertime > 0...")
|
||||
sql = """
|
||||
SELECT ridername, ROUND(AVG(ridertime), 2) AS avg_time
|
||||
FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet', union_by_name = true)
|
||||
WHERE ridertime > 0 AND ridername IS NOT NULL
|
||||
GROUP BY ridername
|
||||
ORDER BY avg_time DESC
|
||||
LIMIT 10
|
||||
"""
|
||||
df = conn.execute(sql).df()
|
||||
if df.empty:
|
||||
print("NO DATA RETURNED!")
|
||||
# Check counts
|
||||
print("Counts for ridertime > 0:")
|
||||
print(conn.execute("SELECT COUNT(*) FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet', union_by_name = true) WHERE ridertime > 0").fetchall())
|
||||
else:
|
||||
print(df)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_test()
|
||||
18
test_s3.py
Normal file
18
test_s3.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import duckdb
|
||||
|
||||
def test_s3():
|
||||
try:
|
||||
conn = duckdb.connect()
|
||||
conn.execute("INSTALL httpfs; LOAD httpfs;")
|
||||
conn.execute("SET s3_region='sgp1';")
|
||||
conn.execute("SET s3_endpoint='sgp1.digitaloceanspaces.com';")
|
||||
conn.execute("SET s3_url_style='path';")
|
||||
|
||||
# Test if we can see the files
|
||||
res = conn.execute("SELECT count(*) FROM read_parquet('s3://nearle/parquet/deliveries/*.parquet')").fetchall()
|
||||
print(f"S3 Count: {res[0][0]}")
|
||||
except Exception as e:
|
||||
print(f"Error accessing S3: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_s3()
|
||||
100
tmp_logs.log
Normal file
100
tmp_logs.log
Normal file
@@ -0,0 +1,100 @@
|
||||
[2026-03-19 17:56:53] DuckDB ready with view 'deliveries' from s3://nearle/parquet/deliveries/*.parquet
|
||||
[2026-03-19 17:56:55] NODE: Semantic Column Selector - Who is the top revenue tenant?
|
||||
[2026-03-19 17:56:55] Selected Columns: ['tenantname', 'orderamount', 'deliveryamt', 'tenantid', 'tenantcontactno', 'tenanttoken', 'tenantsuburb', 'tenantcity', 'tenantaddress', 'othercharges']
|
||||
[2026-03-19 17:56:55] NODE: Intent Detection - Who is the top revenue tenant?
|
||||
[2026-03-19 17:56:55] NODE: Metric Selection (Semantic Layer)
|
||||
[2026-03-19 17:56:55] Semantic Match Found: revenue -> SUM(deliveryamount)
|
||||
[2026-03-19 17:56:55] NODE: Chart Planning
|
||||
[2026-03-19 17:57:31] NODE: SQL Generation (Reduced Schema)
|
||||
[2026-03-19 17:58:29] GEN SQL: Top Performance Comparison -> SELECT
|
||||
DATE(deliverydate) AS day,
|
||||
COUNT(deliveryid) AS delivery_count
|
||||
FROM deliveries
|
||||
WHERE
|
||||
deliverydate >= (
|
||||
SELECT
|
||||
MAX(deliverydate)
|
||||
FROM deliveries
|
||||
) - INTERVAL '30 days'
|
||||
GROUP BY
|
||||
day
|
||||
ORDER BY
|
||||
SUM(deliveryamount) DESC
|
||||
LIMIT 10
|
||||
[2026-03-19 17:58:29] NODE: Batch Execute SQL with Fallback
|
||||
[2026-03-19 17:58:29] Initializing DuckDB with live S3 data
|
||||
[2026-03-19 17:58:31] DuckDB ready with view 'deliveries' from s3://nearle/parquet/deliveries/*.parquet
|
||||
[2026-03-19 17:58:31] EXECUTING SQL for 'Top Performance Comparison':
|
||||
SELECT
|
||||
DATE(deliverydate) AS day,
|
||||
COUNT(deliveryid) AS delivery_count
|
||||
FROM deliveries
|
||||
WHERE
|
||||
deliverydate >= (
|
||||
SELECT
|
||||
MAX(deliverydate)
|
||||
FROM deliveries
|
||||
) - INTERVAL '30 days'
|
||||
GROUP BY
|
||||
day
|
||||
ORDER BY
|
||||
SUM(deliveryamount) DESC
|
||||
LIMIT 10
|
||||
[2026-03-19 17:58:33] EXEC DONE: Top Performance Comparison found 10 rows
|
||||
[2026-03-19 17:58:33] NODE: Forecasting Layer
|
||||
[2026-03-19 17:58:33] NODE: Anomaly Detection
|
||||
[2026-03-19 17:58:33] NODE: Batch Validation & Smart Suggestions
|
||||
[2026-03-19 17:58:33] NODE: Results Analysis
|
||||
[2026-03-19 17:58:57] NODE: Business Insights
|
||||
[2026-03-19 17:59:50] NODE: Dashboard Assembly
|
||||
[2026-03-19 19:00:02] NODE: Dataset Summary
|
||||
[2026-03-19 19:00:02] Initializing DuckDB with live S3 data
|
||||
[2026-03-19 19:00:03] DuckDB ready with view 'deliveries' from s3://nearle/parquet/deliveries/*.parquet
|
||||
[2026-03-19 19:00:06] NODE: Semantic Column Selector - Who is the top revenue tenant?
|
||||
[2026-03-19 19:00:06] Selected Columns: ['tenantname', 'orderamount', 'deliveryamt', 'tenantid', 'tenantcontactno', 'tenanttoken', 'tenantsuburb', 'tenantcity', 'tenantaddress', 'othercharges']
|
||||
[2026-03-19 19:00:06] NODE: Intent Detection - Who is the top revenue tenant?
|
||||
[2026-03-19 19:00:06] NODE: Metric Selection (Semantic Layer)
|
||||
[2026-03-19 19:00:06] Semantic Match Found: revenue -> SUM(deliveryamount)
|
||||
[2026-03-19 19:00:06] NODE: Chart Planning
|
||||
[2026-03-19 19:00:43] NODE: SQL Generation (Reduced Schema)
|
||||
[2026-03-19 19:01:39] GEN SQL: Top Performance Comparison -> SELECT
|
||||
DATE(deliverydate) AS day,
|
||||
COUNT(deliveryid) AS delivery_count
|
||||
FROM deliveries
|
||||
WHERE
|
||||
deliverydate >= (
|
||||
SELECT
|
||||
MAX(deliverydate)
|
||||
FROM deliveries
|
||||
) - INTERVAL '30 days'
|
||||
GROUP BY
|
||||
day
|
||||
ORDER BY
|
||||
SUM(deliveryamount) DESC
|
||||
LIMIT 10
|
||||
[2026-03-19 19:01:39] NODE: Batch Execute SQL with Fallback
|
||||
[2026-03-19 19:01:39] Initializing DuckDB with live S3 data
|
||||
[2026-03-19 19:01:40] DuckDB ready with view 'deliveries' from s3://nearle/parquet/deliveries/*.parquet
|
||||
[2026-03-19 19:01:40] EXECUTING SQL for 'Top Performance Comparison':
|
||||
SELECT
|
||||
DATE(deliverydate) AS day,
|
||||
COUNT(deliveryid) AS delivery_count
|
||||
FROM deliveries
|
||||
WHERE
|
||||
deliverydate >= (
|
||||
SELECT
|
||||
MAX(deliverydate)
|
||||
FROM deliveries
|
||||
) - INTERVAL '30 days'
|
||||
GROUP BY
|
||||
day
|
||||
ORDER BY
|
||||
SUM(deliveryamount) DESC
|
||||
LIMIT 10
|
||||
[2026-03-19 19:01:42] EXEC DONE: Top Performance Comparison found 10 rows
|
||||
[2026-03-19 19:01:42] NODE: Forecasting Layer
|
||||
[2026-03-19 19:01:42] NODE: Anomaly Detection
|
||||
[2026-03-19 19:01:42] NODE: Batch Validation & Smart Suggestions
|
||||
[2026-03-19 19:01:42] NODE: Results Analysis
|
||||
[2026-03-19 19:02:07] NODE: Business Insights
|
||||
[2026-03-19 19:02:59] NODE: Dashboard Assembly
|
||||
164
venv/Include/site/python3.11/greenlet/greenlet.h
Normal file
164
venv/Include/site/python3.11/greenlet/greenlet.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
|
||||
/* Greenlet object interface */
|
||||
|
||||
#ifndef Py_GREENLETOBJECT_H
|
||||
#define Py_GREENLETOBJECT_H
|
||||
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is deprecated and undocumented. It does not change. */
|
||||
#define GREENLET_VERSION "1.0.0"
|
||||
|
||||
#ifndef GREENLET_MODULE
|
||||
#define implementation_ptr_t void*
|
||||
#endif
|
||||
|
||||
typedef struct _greenlet {
|
||||
PyObject_HEAD
|
||||
PyObject* weakreflist;
|
||||
PyObject* dict;
|
||||
implementation_ptr_t pimpl;
|
||||
} PyGreenlet;
|
||||
|
||||
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
|
||||
|
||||
|
||||
/* C API functions */
|
||||
|
||||
/* Total number of symbols that are exported */
|
||||
#define PyGreenlet_API_pointers 12
|
||||
|
||||
#define PyGreenlet_Type_NUM 0
|
||||
#define PyExc_GreenletError_NUM 1
|
||||
#define PyExc_GreenletExit_NUM 2
|
||||
|
||||
#define PyGreenlet_New_NUM 3
|
||||
#define PyGreenlet_GetCurrent_NUM 4
|
||||
#define PyGreenlet_Throw_NUM 5
|
||||
#define PyGreenlet_Switch_NUM 6
|
||||
#define PyGreenlet_SetParent_NUM 7
|
||||
|
||||
#define PyGreenlet_MAIN_NUM 8
|
||||
#define PyGreenlet_STARTED_NUM 9
|
||||
#define PyGreenlet_ACTIVE_NUM 10
|
||||
#define PyGreenlet_GET_PARENT_NUM 11
|
||||
|
||||
#ifndef GREENLET_MODULE
|
||||
/* This section is used by modules that uses the greenlet C API */
|
||||
static void** _PyGreenlet_API = NULL;
|
||||
|
||||
# define PyGreenlet_Type \
|
||||
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
|
||||
|
||||
# define PyExc_GreenletError \
|
||||
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
|
||||
|
||||
# define PyExc_GreenletExit \
|
||||
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_New(PyObject *args)
|
||||
*
|
||||
* greenlet.greenlet(run, parent=None)
|
||||
*/
|
||||
# define PyGreenlet_New \
|
||||
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
|
||||
_PyGreenlet_API[PyGreenlet_New_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_GetCurrent(void)
|
||||
*
|
||||
* greenlet.getcurrent()
|
||||
*/
|
||||
# define PyGreenlet_GetCurrent \
|
||||
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_Throw(
|
||||
* PyGreenlet *greenlet,
|
||||
* PyObject *typ,
|
||||
* PyObject *val,
|
||||
* PyObject *tb)
|
||||
*
|
||||
* g.throw(...)
|
||||
*/
|
||||
# define PyGreenlet_Throw \
|
||||
(*(PyObject * (*)(PyGreenlet * self, \
|
||||
PyObject * typ, \
|
||||
PyObject * val, \
|
||||
PyObject * tb)) \
|
||||
_PyGreenlet_API[PyGreenlet_Throw_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
|
||||
*
|
||||
* g.switch(*args, **kwargs)
|
||||
*/
|
||||
# define PyGreenlet_Switch \
|
||||
(*(PyObject * \
|
||||
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
|
||||
_PyGreenlet_API[PyGreenlet_Switch_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
|
||||
*
|
||||
* g.parent = new_parent
|
||||
*/
|
||||
# define PyGreenlet_SetParent \
|
||||
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
|
||||
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_GetParent(PyObject* greenlet)
|
||||
*
|
||||
* return greenlet.parent;
|
||||
*
|
||||
* This could return NULL even if there is no exception active.
|
||||
* If it does not return NULL, you are responsible for decrementing the
|
||||
* reference count.
|
||||
*/
|
||||
# define PyGreenlet_GetParent \
|
||||
(*(PyGreenlet* (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
|
||||
|
||||
/*
|
||||
* deprecated, undocumented alias.
|
||||
*/
|
||||
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
|
||||
|
||||
# define PyGreenlet_MAIN \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
|
||||
|
||||
# define PyGreenlet_STARTED \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
|
||||
|
||||
# define PyGreenlet_ACTIVE \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
|
||||
|
||||
|
||||
|
||||
|
||||
/* Macro that imports greenlet and initializes C API */
|
||||
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
|
||||
keep the older definition to be sure older code that might have a copy of
|
||||
the header still works. */
|
||||
# define PyGreenlet_Import() \
|
||||
{ \
|
||||
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
|
||||
}
|
||||
|
||||
#endif /* GREENLET_MODULE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !Py_GREENLETOBJECT_H */
|
||||
BIN
venv/Lib/site-packages/__pycache__/jsonpatch.cpython-311.pyc
Normal file
BIN
venv/Lib/site-packages/__pycache__/jsonpatch.cpython-311.pyc
Normal file
Binary file not shown.
BIN
venv/Lib/site-packages/__pycache__/jsonpointer.cpython-311.pyc
Normal file
BIN
venv/Lib/site-packages/__pycache__/jsonpointer.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
venv/Lib/site-packages/__pycache__/six.cpython-311.pyc
Normal file
BIN
venv/Lib/site-packages/__pycache__/six.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
222
venv/Lib/site-packages/_distutils_hack/__init__.py
Normal file
222
venv/Lib/site-packages/_distutils_hack/__init__.py
Normal file
@@ -0,0 +1,222 @@
|
||||
# don't import any costly modules
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
is_pypy = '__pypy__' in sys.builtin_module_names
|
||||
|
||||
|
||||
def warn_distutils_present():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
if is_pypy and sys.version_info < (3, 7):
|
||||
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
|
||||
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
|
||||
return
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"Distutils was imported before Setuptools, but importing Setuptools "
|
||||
"also replaces the `distutils` module in `sys.modules`. This may lead "
|
||||
"to undesirable behaviors or errors. To avoid these issues, avoid "
|
||||
"using distutils directly, ensure that setuptools is installed in the "
|
||||
"traditional way (e.g. not an editable install), and/or make sure "
|
||||
"that setuptools is always imported before distutils."
|
||||
)
|
||||
|
||||
|
||||
def clear_distutils():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
import warnings
|
||||
|
||||
warnings.warn("Setuptools is replacing distutils.")
|
||||
mods = [
|
||||
name
|
||||
for name in sys.modules
|
||||
if name == "distutils" or name.startswith("distutils.")
|
||||
]
|
||||
for name in mods:
|
||||
del sys.modules[name]
|
||||
|
||||
|
||||
def enabled():
|
||||
"""
|
||||
Allow selection of distutils by environment variable.
|
||||
"""
|
||||
which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
|
||||
return which == 'local'
|
||||
|
||||
|
||||
def ensure_local_distutils():
|
||||
import importlib
|
||||
|
||||
clear_distutils()
|
||||
|
||||
# With the DistutilsMetaFinder in place,
|
||||
# perform an import to cause distutils to be
|
||||
# loaded from setuptools._distutils. Ref #2906.
|
||||
with shim():
|
||||
importlib.import_module('distutils')
|
||||
|
||||
# check that submodules load as expected
|
||||
core = importlib.import_module('distutils.core')
|
||||
assert '_distutils' in core.__file__, core.__file__
|
||||
assert 'setuptools._distutils.log' not in sys.modules
|
||||
|
||||
|
||||
def do_override():
|
||||
"""
|
||||
Ensure that the local copy of distutils is preferred over stdlib.
|
||||
|
||||
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
|
||||
for more motivation.
|
||||
"""
|
||||
if enabled():
|
||||
warn_distutils_present()
|
||||
ensure_local_distutils()
|
||||
|
||||
|
||||
class _TrivialRe:
|
||||
def __init__(self, *patterns):
|
||||
self._patterns = patterns
|
||||
|
||||
def match(self, string):
|
||||
return all(pat in string for pat in self._patterns)
|
||||
|
||||
|
||||
class DistutilsMetaFinder:
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
# optimization: only consider top level modules and those
|
||||
# found in the CPython test suite.
|
||||
if path is not None and not fullname.startswith('test.'):
|
||||
return
|
||||
|
||||
method_name = 'spec_for_{fullname}'.format(**locals())
|
||||
method = getattr(self, method_name, lambda: None)
|
||||
return method()
|
||||
|
||||
def spec_for_distutils(self):
|
||||
if self.is_cpython():
|
||||
return
|
||||
|
||||
import importlib
|
||||
import importlib.abc
|
||||
import importlib.util
|
||||
|
||||
try:
|
||||
mod = importlib.import_module('setuptools._distutils')
|
||||
except Exception:
|
||||
# There are a couple of cases where setuptools._distutils
|
||||
# may not be present:
|
||||
# - An older Setuptools without a local distutils is
|
||||
# taking precedence. Ref #2957.
|
||||
# - Path manipulation during sitecustomize removes
|
||||
# setuptools from the path but only after the hook
|
||||
# has been loaded. Ref #2980.
|
||||
# In either case, fall back to stdlib behavior.
|
||||
return
|
||||
|
||||
class DistutilsLoader(importlib.abc.Loader):
|
||||
def create_module(self, spec):
|
||||
mod.__name__ = 'distutils'
|
||||
return mod
|
||||
|
||||
def exec_module(self, module):
|
||||
pass
|
||||
|
||||
return importlib.util.spec_from_loader(
|
||||
'distutils', DistutilsLoader(), origin=mod.__file__
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def is_cpython():
|
||||
"""
|
||||
Suppress supplying distutils for CPython (build and tests).
|
||||
Ref #2965 and #3007.
|
||||
"""
|
||||
return os.path.isfile('pybuilddir.txt')
|
||||
|
||||
def spec_for_pip(self):
|
||||
"""
|
||||
Ensure stdlib distutils when running under pip.
|
||||
See pypa/pip#8761 for rationale.
|
||||
"""
|
||||
if self.pip_imported_during_build():
|
||||
return
|
||||
clear_distutils()
|
||||
self.spec_for_distutils = lambda: None
|
||||
|
||||
@classmethod
|
||||
def pip_imported_during_build(cls):
|
||||
"""
|
||||
Detect if pip is being imported in a build script. Ref #2355.
|
||||
"""
|
||||
import traceback
|
||||
|
||||
return any(
|
||||
cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def frame_file_is_setup(frame):
|
||||
"""
|
||||
Return True if the indicated frame suggests a setup.py file.
|
||||
"""
|
||||
# some frames may not have __file__ (#2940)
|
||||
return frame.f_globals.get('__file__', '').endswith('setup.py')
|
||||
|
||||
def spec_for_sensitive_tests(self):
|
||||
"""
|
||||
Ensure stdlib distutils when running select tests under CPython.
|
||||
|
||||
python/cpython#91169
|
||||
"""
|
||||
clear_distutils()
|
||||
self.spec_for_distutils = lambda: None
|
||||
|
||||
sensitive_tests = (
|
||||
[
|
||||
'test.test_distutils',
|
||||
'test.test_peg_generator',
|
||||
'test.test_importlib',
|
||||
]
|
||||
if sys.version_info < (3, 10)
|
||||
else [
|
||||
'test.test_distutils',
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
for name in DistutilsMetaFinder.sensitive_tests:
|
||||
setattr(
|
||||
DistutilsMetaFinder,
|
||||
f'spec_for_{name}',
|
||||
DistutilsMetaFinder.spec_for_sensitive_tests,
|
||||
)
|
||||
|
||||
|
||||
DISTUTILS_FINDER = DistutilsMetaFinder()
|
||||
|
||||
|
||||
def add_shim():
|
||||
DISTUTILS_FINDER in sys.meta_path or insert_shim()
|
||||
|
||||
|
||||
class shim:
|
||||
def __enter__(self):
|
||||
insert_shim()
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
remove_shim()
|
||||
|
||||
|
||||
def insert_shim():
|
||||
sys.meta_path.insert(0, DISTUTILS_FINDER)
|
||||
|
||||
|
||||
def remove_shim():
|
||||
try:
|
||||
sys.meta_path.remove(DISTUTILS_FINDER)
|
||||
except ValueError:
|
||||
pass
|
||||
Binary file not shown.
Binary file not shown.
1
venv/Lib/site-packages/_distutils_hack/override.py
Normal file
1
venv/Lib/site-packages/_distutils_hack/override.py
Normal file
@@ -0,0 +1 @@
|
||||
__import__('_distutils_hack').do_override()
|
||||
1479
venv/Lib/site-packages/_duckdb-stubs/__init__.pyi
Normal file
1479
venv/Lib/site-packages/_duckdb-stubs/__init__.pyi
Normal file
File diff suppressed because it is too large
Load Diff
46
venv/Lib/site-packages/_duckdb-stubs/_func.pyi
Normal file
46
venv/Lib/site-packages/_duckdb-stubs/_func.pyi
Normal file
@@ -0,0 +1,46 @@
|
||||
import typing as pytyping
|
||||
|
||||
__all__: list[str] = ["ARROW", "DEFAULT", "NATIVE", "SPECIAL", "FunctionNullHandling", "PythonUDFType"]
|
||||
|
||||
class FunctionNullHandling:
|
||||
DEFAULT: pytyping.ClassVar[FunctionNullHandling] # value = <FunctionNullHandling.DEFAULT: 0>
|
||||
SPECIAL: pytyping.ClassVar[FunctionNullHandling] # value = <FunctionNullHandling.SPECIAL: 1>
|
||||
__members__: pytyping.ClassVar[
|
||||
dict[str, FunctionNullHandling]
|
||||
] # value = {'DEFAULT': <FunctionNullHandling.DEFAULT: 0>, 'SPECIAL': <FunctionNullHandling.SPECIAL: 1>}
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
def __getstate__(self) -> int: ...
|
||||
def __hash__(self) -> int: ...
|
||||
def __index__(self) -> int: ...
|
||||
def __init__(self, value: pytyping.SupportsInt) -> None: ...
|
||||
def __int__(self) -> int: ...
|
||||
def __ne__(self, other: object) -> bool: ...
|
||||
def __setstate__(self, state: pytyping.SupportsInt) -> None: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
@property
|
||||
def value(self) -> int: ...
|
||||
|
||||
class PythonUDFType:
|
||||
ARROW: pytyping.ClassVar[PythonUDFType] # value = <PythonUDFType.ARROW: 1>
|
||||
NATIVE: pytyping.ClassVar[PythonUDFType] # value = <PythonUDFType.NATIVE: 0>
|
||||
__members__: pytyping.ClassVar[
|
||||
dict[str, PythonUDFType]
|
||||
] # value = {'NATIVE': <PythonUDFType.NATIVE: 0>, 'ARROW': <PythonUDFType.ARROW: 1>}
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
def __getstate__(self) -> int: ...
|
||||
def __hash__(self) -> int: ...
|
||||
def __index__(self) -> int: ...
|
||||
def __init__(self, value: pytyping.SupportsInt) -> None: ...
|
||||
def __int__(self) -> int: ...
|
||||
def __ne__(self, other: object) -> bool: ...
|
||||
def __setstate__(self, state: pytyping.SupportsInt) -> None: ...
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
@property
|
||||
def value(self) -> int: ...
|
||||
|
||||
ARROW: PythonUDFType # value = <PythonUDFType.ARROW: 1>
|
||||
DEFAULT: FunctionNullHandling # value = <FunctionNullHandling.DEFAULT: 0>
|
||||
NATIVE: PythonUDFType # value = <PythonUDFType.NATIVE: 0>
|
||||
SPECIAL: FunctionNullHandling # value = <FunctionNullHandling.SPECIAL: 1>
|
||||
75
venv/Lib/site-packages/_duckdb-stubs/_sqltypes.pyi
Normal file
75
venv/Lib/site-packages/_duckdb-stubs/_sqltypes.pyi
Normal file
@@ -0,0 +1,75 @@
|
||||
import duckdb
|
||||
import typing as pytyping
|
||||
|
||||
__all__: list[str] = [
|
||||
"BIGINT",
|
||||
"BIT",
|
||||
"BLOB",
|
||||
"BOOLEAN",
|
||||
"DATE",
|
||||
"DOUBLE",
|
||||
"FLOAT",
|
||||
"HUGEINT",
|
||||
"INTEGER",
|
||||
"INTERVAL",
|
||||
"SMALLINT",
|
||||
"SQLNULL",
|
||||
"TIME",
|
||||
"TIMESTAMP",
|
||||
"TIMESTAMP_MS",
|
||||
"TIMESTAMP_NS",
|
||||
"TIMESTAMP_S",
|
||||
"TIMESTAMP_TZ",
|
||||
"TIME_TZ",
|
||||
"TINYINT",
|
||||
"UBIGINT",
|
||||
"UHUGEINT",
|
||||
"UINTEGER",
|
||||
"USMALLINT",
|
||||
"UTINYINT",
|
||||
"UUID",
|
||||
"VARCHAR",
|
||||
"DuckDBPyType",
|
||||
]
|
||||
|
||||
class DuckDBPyType:
|
||||
def __eq__(self, other: object) -> bool: ...
|
||||
def __getattr__(self, name: str) -> DuckDBPyType: ...
|
||||
def __getitem__(self, name: str) -> DuckDBPyType: ...
|
||||
def __hash__(self) -> int: ...
|
||||
@pytyping.overload
|
||||
def __init__(self, type_str: str, connection: duckdb.DuckDBPyConnection) -> None: ...
|
||||
@pytyping.overload
|
||||
def __init__(self, obj: object) -> None: ...
|
||||
@property
|
||||
def children(self) -> list[tuple[str, object]]: ...
|
||||
@property
|
||||
def id(self) -> str: ...
|
||||
|
||||
BIGINT: DuckDBPyType # value = BIGINT
|
||||
BIT: DuckDBPyType # value = BIT
|
||||
BLOB: DuckDBPyType # value = BLOB
|
||||
BOOLEAN: DuckDBPyType # value = BOOLEAN
|
||||
DATE: DuckDBPyType # value = DATE
|
||||
DOUBLE: DuckDBPyType # value = DOUBLE
|
||||
FLOAT: DuckDBPyType # value = FLOAT
|
||||
HUGEINT: DuckDBPyType # value = HUGEINT
|
||||
INTEGER: DuckDBPyType # value = INTEGER
|
||||
INTERVAL: DuckDBPyType # value = INTERVAL
|
||||
SMALLINT: DuckDBPyType # value = SMALLINT
|
||||
SQLNULL: DuckDBPyType # value = "NULL"
|
||||
TIME: DuckDBPyType # value = TIME
|
||||
TIMESTAMP: DuckDBPyType # value = TIMESTAMP
|
||||
TIMESTAMP_MS: DuckDBPyType # value = TIMESTAMP_MS
|
||||
TIMESTAMP_NS: DuckDBPyType # value = TIMESTAMP_NS
|
||||
TIMESTAMP_S: DuckDBPyType # value = TIMESTAMP_S
|
||||
TIMESTAMP_TZ: DuckDBPyType # value = TIMESTAMP WITH TIME ZONE
|
||||
TIME_TZ: DuckDBPyType # value = TIME WITH TIME ZONE
|
||||
TINYINT: DuckDBPyType # value = TINYINT
|
||||
UBIGINT: DuckDBPyType # value = UBIGINT
|
||||
UHUGEINT: DuckDBPyType # value = UHUGEINT
|
||||
UINTEGER: DuckDBPyType # value = UINTEGER
|
||||
USMALLINT: DuckDBPyType # value = USMALLINT
|
||||
UTINYINT: DuckDBPyType # value = UTINYINT
|
||||
UUID: DuckDBPyType # value = UUID
|
||||
VARCHAR: DuckDBPyType # value = VARCHAR
|
||||
BIN
venv/Lib/site-packages/_duckdb.cp311-win_amd64.pyd
Normal file
BIN
venv/Lib/site-packages/_duckdb.cp311-win_amd64.pyd
Normal file
Binary file not shown.
33
venv/Lib/site-packages/_yaml/__init__.py
Normal file
33
venv/Lib/site-packages/_yaml/__init__.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# This is a stub package designed to roughly emulate the _yaml
|
||||
# extension module, which previously existed as a standalone module
|
||||
# and has been moved into the `yaml` package namespace.
|
||||
# It does not perfectly mimic its old counterpart, but should get
|
||||
# close enough for anyone who's relying on it even when they shouldn't.
|
||||
import yaml
|
||||
|
||||
# in some circumstances, the yaml module we imoprted may be from a different version, so we need
|
||||
# to tread carefully when poking at it here (it may not have the attributes we expect)
|
||||
if not getattr(yaml, '__with_libyaml__', False):
|
||||
from sys import version_info
|
||||
|
||||
exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
|
||||
raise exc("No module named '_yaml'")
|
||||
else:
|
||||
from yaml._yaml import *
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'The _yaml extension module is now located at yaml._yaml'
|
||||
' and its location is subject to change. To use the'
|
||||
' LibYAML-based parser and emitter, import from `yaml`:'
|
||||
' `from yaml import CLoader as Loader, CDumper as Dumper`.',
|
||||
DeprecationWarning
|
||||
)
|
||||
del warnings
|
||||
# Don't `del yaml` here because yaml is actually an existing
|
||||
# namespace member of _yaml.
|
||||
|
||||
__name__ = '_yaml'
|
||||
# If the module is top-level (i.e. not a part of any specific package)
|
||||
# then the attribute should be set to ''.
|
||||
# https://docs.python.org/3.8/library/types.html
|
||||
__package__ = ''
|
||||
Binary file not shown.
49
venv/Lib/site-packages/adbc_driver_duckdb/__init__.py
Normal file
49
venv/Lib/site-packages/adbc_driver_duckdb/__init__.py
Normal file
@@ -0,0 +1,49 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Low-level ADBC bindings for the DuckDB driver."""
|
||||
|
||||
import enum
|
||||
import functools
|
||||
import importlib.util
|
||||
import typing
|
||||
|
||||
import adbc_driver_manager
|
||||
|
||||
|
||||
class StatementOptions(enum.Enum):
|
||||
"""Statement options specific to the DuckDB driver."""
|
||||
|
||||
#: The number of rows per batch. Defaults to 2048.
|
||||
BATCH_ROWS = "adbc.duckdb.query.batch_rows"
|
||||
|
||||
|
||||
def connect(path: typing.Optional[str] = None) -> adbc_driver_manager.AdbcDatabase:
|
||||
"""Create a low level ADBC connection to DuckDB."""
|
||||
if path is None:
|
||||
return adbc_driver_manager.AdbcDatabase(driver=driver_path(), entrypoint="duckdb_adbc_init")
|
||||
return adbc_driver_manager.AdbcDatabase(driver=driver_path(), entrypoint="duckdb_adbc_init", path=path)
|
||||
|
||||
|
||||
@functools.cache
|
||||
def driver_path() -> str:
|
||||
"""Get the path to the DuckDB ADBC driver."""
|
||||
duckdb_module_spec = importlib.util.find_spec("_duckdb")
|
||||
if duckdb_module_spec is None:
|
||||
msg = "Could not find duckdb shared library. Did you pip install duckdb?"
|
||||
raise ImportError(msg)
|
||||
return duckdb_module_spec.origin
|
||||
Binary file not shown.
Binary file not shown.
115
venv/Lib/site-packages/adbc_driver_duckdb/dbapi.py
Normal file
115
venv/Lib/site-packages/adbc_driver_duckdb/dbapi.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""DBAPI 2.0-compatible facade for the ADBC DuckDB driver."""
|
||||
|
||||
import typing
|
||||
|
||||
import adbc_driver_manager
|
||||
import adbc_driver_manager.dbapi
|
||||
|
||||
import adbc_driver_duckdb
|
||||
|
||||
__all__ = [
|
||||
"BINARY",
|
||||
"DATETIME",
|
||||
"NUMBER",
|
||||
"ROWID",
|
||||
"STRING",
|
||||
"Connection",
|
||||
"Cursor",
|
||||
"DataError",
|
||||
"DatabaseError",
|
||||
"Date",
|
||||
"DateFromTicks",
|
||||
"Error",
|
||||
"IntegrityError",
|
||||
"InterfaceError",
|
||||
"InternalError",
|
||||
"NotSupportedError",
|
||||
"OperationalError",
|
||||
"ProgrammingError",
|
||||
"Time",
|
||||
"TimeFromTicks",
|
||||
"Timestamp",
|
||||
"TimestampFromTicks",
|
||||
"Warning",
|
||||
"apilevel",
|
||||
"connect",
|
||||
"paramstyle",
|
||||
"threadsafety",
|
||||
]
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Globals
|
||||
|
||||
apilevel = adbc_driver_manager.dbapi.apilevel
|
||||
threadsafety = adbc_driver_manager.dbapi.threadsafety
|
||||
paramstyle = "qmark"
|
||||
|
||||
Warning = adbc_driver_manager.dbapi.Warning
|
||||
Error = adbc_driver_manager.dbapi.Error
|
||||
InterfaceError = adbc_driver_manager.dbapi.InterfaceError
|
||||
DatabaseError = adbc_driver_manager.dbapi.DatabaseError
|
||||
DataError = adbc_driver_manager.dbapi.DataError
|
||||
OperationalError = adbc_driver_manager.dbapi.OperationalError
|
||||
IntegrityError = adbc_driver_manager.dbapi.IntegrityError
|
||||
InternalError = adbc_driver_manager.dbapi.InternalError
|
||||
ProgrammingError = adbc_driver_manager.dbapi.ProgrammingError
|
||||
NotSupportedError = adbc_driver_manager.dbapi.NotSupportedError
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Types
|
||||
|
||||
Date = adbc_driver_manager.dbapi.Date
|
||||
Time = adbc_driver_manager.dbapi.Time
|
||||
Timestamp = adbc_driver_manager.dbapi.Timestamp
|
||||
DateFromTicks = adbc_driver_manager.dbapi.DateFromTicks
|
||||
TimeFromTicks = adbc_driver_manager.dbapi.TimeFromTicks
|
||||
TimestampFromTicks = adbc_driver_manager.dbapi.TimestampFromTicks
|
||||
STRING = adbc_driver_manager.dbapi.STRING
|
||||
BINARY = adbc_driver_manager.dbapi.BINARY
|
||||
NUMBER = adbc_driver_manager.dbapi.NUMBER
|
||||
DATETIME = adbc_driver_manager.dbapi.DATETIME
|
||||
ROWID = adbc_driver_manager.dbapi.ROWID
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Functions
|
||||
|
||||
|
||||
def connect(path: typing.Optional[str] = None, **kwargs) -> "Connection":
|
||||
"""Connect to DuckDB via ADBC."""
|
||||
db = None
|
||||
conn = None
|
||||
|
||||
try:
|
||||
db = adbc_driver_duckdb.connect(path)
|
||||
conn = adbc_driver_manager.AdbcConnection(db)
|
||||
return adbc_driver_manager.dbapi.Connection(db, conn, **kwargs)
|
||||
except Exception:
|
||||
if conn:
|
||||
conn.close()
|
||||
if db:
|
||||
db.close()
|
||||
raise
|
||||
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Classes
|
||||
|
||||
Connection = adbc_driver_manager.dbapi.Connection
|
||||
Cursor = adbc_driver_manager.dbapi.Cursor
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
279
venv/Lib/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE
Normal file
279
venv/Lib/site-packages/aiohappyeyeballs-2.6.1.dist-info/LICENSE
Normal file
@@ -0,0 +1,279 @@
|
||||
A. HISTORY OF THE SOFTWARE
|
||||
==========================
|
||||
|
||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||
Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
|
||||
as a successor of a language called ABC. Guido remains Python's
|
||||
principal author, although it includes many contributions from others.
|
||||
|
||||
In 1995, Guido continued his work on Python at the Corporation for
|
||||
National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
|
||||
in Reston, Virginia where he released several versions of the
|
||||
software.
|
||||
|
||||
In May 2000, Guido and the Python core development team moved to
|
||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||
year, the PythonLabs team moved to Digital Creations, which became
|
||||
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
|
||||
https://www.python.org/psf/) was formed, a non-profit organization
|
||||
created specifically to own Python-related Intellectual Property.
|
||||
Zope Corporation was a sponsoring member of the PSF.
|
||||
|
||||
All Python releases are Open Source (see https://opensource.org for
|
||||
the Open Source Definition). Historically, most, but not all, Python
|
||||
releases have also been GPL-compatible; the table below summarizes
|
||||
the various releases.
|
||||
|
||||
Release Derived Year Owner GPL-
|
||||
from compatible? (1)
|
||||
|
||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||
1.6 1.5.2 2000 CNRI no
|
||||
2.0 1.6 2000 BeOpen.com no
|
||||
1.6.1 1.6 2001 CNRI yes (2)
|
||||
2.1 2.0+1.6.1 2001 PSF no
|
||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||
2.1.2 2.1.1 2002 PSF yes
|
||||
2.1.3 2.1.2 2002 PSF yes
|
||||
2.2 and above 2.1.1 2001-now PSF yes
|
||||
|
||||
Footnotes:
|
||||
|
||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||
a modified version without making your changes open source. The
|
||||
GPL-compatible licenses make it possible to combine Python with
|
||||
other software that is released under the GPL; the others don't.
|
||||
|
||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||
because its license has a choice of law clause. According to
|
||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||
is "not incompatible" with the GPL.
|
||||
|
||||
Thanks to the many outside volunteers who have worked under Guido's
|
||||
direction to make these releases possible.
|
||||
|
||||
|
||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||
===============================================================
|
||||
|
||||
Python software and documentation are licensed under the
|
||||
Python Software Foundation License Version 2.
|
||||
|
||||
Starting with Python 3.8.6, examples, recipes, and other code in
|
||||
the documentation are dual licensed under the PSF License Version 2
|
||||
and the Zero-Clause BSD license.
|
||||
|
||||
Some software incorporated into Python is under different licenses.
|
||||
The licenses are listed with code falling under that license.
|
||||
|
||||
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||
-------------------------------------------
|
||||
|
||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||
|
||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||
Individual or Organization ("Licensee") accessing and otherwise using
|
||||
this software in source or binary form and its associated
|
||||
documentation ("the Software").
|
||||
|
||||
2. Subject to the terms and conditions of this BeOpen Python License
|
||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||
and/or display publicly, prepare derivative works, distribute, and
|
||||
otherwise use the Software alone or in any derivative version,
|
||||
provided, however, that the BeOpen Python License is retained in the
|
||||
Software, alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
5. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
6. This License Agreement shall be governed by and interpreted in all
|
||||
respects by the law of the State of California, excluding conflict of
|
||||
law provisions. Nothing in this License Agreement shall be deemed to
|
||||
create any relationship of agency, partnership, or joint venture
|
||||
between BeOpen and Licensee. This License Agreement does not grant
|
||||
permission to use BeOpen trademarks or trade names in a trademark
|
||||
sense to endorse or promote products or services of Licensee, or any
|
||||
third party. As an exception, the "BeOpen Python" logos available at
|
||||
http://www.pythonlabs.com/logos.html may be used according to the
|
||||
permissions granted on that web page.
|
||||
|
||||
7. By copying, installing or otherwise using the software, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||
---------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||
source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||
alone or in any derivative version, provided, however, that CNRI's
|
||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||
Agreement, Licensee may substitute the following text (omitting the
|
||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||
conditions in CNRI's License Agreement. This Agreement together with
|
||||
Python 1.6.1 may be located on the internet using the following
|
||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||
Agreement may also be obtained from a proxy server on the internet
|
||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 1.6.1.
|
||||
|
||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. This License Agreement shall be governed by the federal
|
||||
intellectual property law of the United States, including without
|
||||
limitation the federal copyright law, and, to the extent such
|
||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||
Virginia, excluding Virginia's conflict of law provisions.
|
||||
Notwithstanding the foregoing, with regard to derivative works based
|
||||
on Python 1.6.1 that incorporate non-separable material that was
|
||||
previously distributed under the GNU General Public License (GPL), the
|
||||
law of the Commonwealth of Virginia shall govern this License
|
||||
Agreement only as to issues arising under or with respect to
|
||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||
License Agreement shall be deemed to create any relationship of
|
||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||
License Agreement does not grant permission to use CNRI trademarks or
|
||||
trade name in a trademark sense to endorse or promote products or
|
||||
services of Licensee, or any third party.
|
||||
|
||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
ACCEPT
|
||||
|
||||
|
||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||
--------------------------------------------------
|
||||
|
||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||
The Netherlands. All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
provided that the above copyright notice appear in all copies and that
|
||||
both that copyright notice and this permission notice appear in
|
||||
supporting documentation, and that the name of Stichting Mathematisch
|
||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||
distribution of the software without specific, written prior
|
||||
permission.
|
||||
|
||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
123
venv/Lib/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA
Normal file
123
venv/Lib/site-packages/aiohappyeyeballs-2.6.1.dist-info/METADATA
Normal file
@@ -0,0 +1,123 @@
|
||||
Metadata-Version: 2.3
|
||||
Name: aiohappyeyeballs
|
||||
Version: 2.6.1
|
||||
Summary: Happy Eyeballs for asyncio
|
||||
License: PSF-2.0
|
||||
Author: J. Nick Koston
|
||||
Author-email: nick@koston.org
|
||||
Requires-Python: >=3.9
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Software Development :: Libraries
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: License :: OSI Approved :: Python Software Foundation License
|
||||
Project-URL: Bug Tracker, https://github.com/aio-libs/aiohappyeyeballs/issues
|
||||
Project-URL: Changelog, https://github.com/aio-libs/aiohappyeyeballs/blob/main/CHANGELOG.md
|
||||
Project-URL: Documentation, https://aiohappyeyeballs.readthedocs.io
|
||||
Project-URL: Repository, https://github.com/aio-libs/aiohappyeyeballs
|
||||
Description-Content-Type: text/markdown
|
||||
|
||||
# aiohappyeyeballs
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/aio-libs/aiohappyeyeballs/actions/workflows/ci.yml?query=branch%3Amain">
|
||||
<img src="https://img.shields.io/github/actions/workflow/status/aio-libs/aiohappyeyeballs/ci-cd.yml?branch=main&label=CI&logo=github&style=flat-square" alt="CI Status" >
|
||||
</a>
|
||||
<a href="https://aiohappyeyeballs.readthedocs.io">
|
||||
<img src="https://img.shields.io/readthedocs/aiohappyeyeballs.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/aio-libs/aiohappyeyeballs">
|
||||
<img src="https://img.shields.io/codecov/c/github/aio-libs/aiohappyeyeballs.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://python-poetry.org/">
|
||||
<img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
|
||||
</a>
|
||||
<a href="https://github.com/astral-sh/ruff">
|
||||
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff">
|
||||
</a>
|
||||
<a href="https://github.com/pre-commit/pre-commit">
|
||||
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://pypi.org/project/aiohappyeyeballs/">
|
||||
<img src="https://img.shields.io/pypi/v/aiohappyeyeballs.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
|
||||
</a>
|
||||
<img src="https://img.shields.io/pypi/pyversions/aiohappyeyeballs.svg?style=flat-square&logo=python&logoColor=fff" alt="Supported Python versions">
|
||||
<img src="https://img.shields.io/pypi/l/aiohappyeyeballs.svg?style=flat-square" alt="License">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**Documentation**: <a href="https://aiohappyeyeballs.readthedocs.io" target="_blank">https://aiohappyeyeballs.readthedocs.io </a>
|
||||
|
||||
**Source Code**: <a href="https://github.com/aio-libs/aiohappyeyeballs" target="_blank">https://github.com/aio-libs/aiohappyeyeballs </a>
|
||||
|
||||
---
|
||||
|
||||
[Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs)
|
||||
([RFC 8305](https://www.rfc-editor.org/rfc/rfc8305.html))
|
||||
|
||||
## Use case
|
||||
|
||||
This library exists to allow connecting with
|
||||
[Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs)
|
||||
([RFC 8305](https://www.rfc-editor.org/rfc/rfc8305.html))
|
||||
when you
|
||||
already have a list of addrinfo and not a DNS name.
|
||||
|
||||
The stdlib version of `loop.create_connection()`
|
||||
will only work when you pass in an unresolved name which
|
||||
is not a good fit when using DNS caching or resolving
|
||||
names via another method such as `zeroconf`.
|
||||
|
||||
## Installation
|
||||
|
||||
Install this via pip (or your favourite package manager):
|
||||
|
||||
`pip install aiohappyeyeballs`
|
||||
|
||||
## License
|
||||
|
||||
[aiohappyeyeballs is licensed under the same terms as cpython itself.](https://github.com/python/cpython/blob/main/LICENSE)
|
||||
|
||||
## Example usage
|
||||
|
||||
```python
|
||||
|
||||
addr_infos = await loop.getaddrinfo("example.org", 80)
|
||||
|
||||
socket = await start_connection(addr_infos)
|
||||
socket = await start_connection(addr_infos, local_addr_infos=local_addr_infos, happy_eyeballs_delay=0.2)
|
||||
|
||||
transport, protocol = await loop.create_connection(
|
||||
MyProtocol, sock=socket, ...)
|
||||
|
||||
# Remove the first address for each family from addr_info
|
||||
pop_addr_infos_interleave(addr_info, 1)
|
||||
|
||||
# Remove all matching address from addr_info
|
||||
remove_addr_infos(addr_info, "dead::beef::")
|
||||
|
||||
# Convert a local_addr to local_addr_infos
|
||||
local_addr_infos = addr_to_addr_infos(("127.0.0.1",0))
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
This package contains code from cpython and is licensed under the same terms as cpython itself.
|
||||
|
||||
This package was created with
|
||||
[Copier](https://copier.readthedocs.io/) and the
|
||||
[browniebroke/pypackage-template](https://github.com/browniebroke/pypackage-template)
|
||||
project template.
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
aiohappyeyeballs-2.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
aiohappyeyeballs-2.6.1.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
|
||||
aiohappyeyeballs-2.6.1.dist-info/METADATA,sha256=NSXlhJwAfi380eEjAo7BQ4P_TVal9xi0qkyZWibMsVM,5915
|
||||
aiohappyeyeballs-2.6.1.dist-info/RECORD,,
|
||||
aiohappyeyeballs-2.6.1.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
||||
aiohappyeyeballs/__init__.py,sha256=x7kktHEtaD9quBcWDJPuLeKyjuVAI-Jj14S9B_5hcTs,361
|
||||
aiohappyeyeballs/__pycache__/__init__.cpython-311.pyc,,
|
||||
aiohappyeyeballs/__pycache__/_staggered.cpython-311.pyc,,
|
||||
aiohappyeyeballs/__pycache__/impl.cpython-311.pyc,,
|
||||
aiohappyeyeballs/__pycache__/types.cpython-311.pyc,,
|
||||
aiohappyeyeballs/__pycache__/utils.cpython-311.pyc,,
|
||||
aiohappyeyeballs/_staggered.py,sha256=edfVowFx-P-ywJjIEF3MdPtEMVODujV6CeMYr65otac,6900
|
||||
aiohappyeyeballs/impl.py,sha256=Dlcm2mTJ28ucrGnxkb_fo9CZzLAkOOBizOt7dreBbXE,9681
|
||||
aiohappyeyeballs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
aiohappyeyeballs/types.py,sha256=YZJIAnyoV4Dz0WFtlaf_OyE4EW7Xus1z7aIfNI6tDDQ,425
|
||||
aiohappyeyeballs/utils.py,sha256=on9GxIR0LhEfZu8P6Twi9hepX9zDanuZM20MWsb3xlQ,3028
|
||||
@@ -0,0 +1,4 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: poetry-core 2.1.1
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
14
venv/Lib/site-packages/aiohappyeyeballs/__init__.py
Normal file
14
venv/Lib/site-packages/aiohappyeyeballs/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
__version__ = "2.6.1"
|
||||
|
||||
from .impl import start_connection
|
||||
from .types import AddrInfoType, SocketFactoryType
|
||||
from .utils import addr_to_addr_infos, pop_addr_infos_interleave, remove_addr_infos
|
||||
|
||||
__all__ = (
|
||||
"AddrInfoType",
|
||||
"SocketFactoryType",
|
||||
"addr_to_addr_infos",
|
||||
"pop_addr_infos_interleave",
|
||||
"remove_addr_infos",
|
||||
"start_connection",
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
207
venv/Lib/site-packages/aiohappyeyeballs/_staggered.py
Normal file
207
venv/Lib/site-packages/aiohappyeyeballs/_staggered.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import asyncio
|
||||
import contextlib
|
||||
|
||||
# PY3.9: Import Callable from typing until we drop Python 3.9 support
|
||||
# https://github.com/python/cpython/issues/87131
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
Set,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
RE_RAISE_EXCEPTIONS = (SystemExit, KeyboardInterrupt)
|
||||
|
||||
|
||||
def _set_result(wait_next: "asyncio.Future[None]") -> None:
|
||||
"""Set the result of a future if it is not already done."""
|
||||
if not wait_next.done():
|
||||
wait_next.set_result(None)
|
||||
|
||||
|
||||
async def _wait_one(
|
||||
futures: "Iterable[asyncio.Future[Any]]",
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
) -> _T:
|
||||
"""Wait for the first future to complete."""
|
||||
wait_next = loop.create_future()
|
||||
|
||||
def _on_completion(fut: "asyncio.Future[Any]") -> None:
|
||||
if not wait_next.done():
|
||||
wait_next.set_result(fut)
|
||||
|
||||
for f in futures:
|
||||
f.add_done_callback(_on_completion)
|
||||
|
||||
try:
|
||||
return await wait_next
|
||||
finally:
|
||||
for f in futures:
|
||||
f.remove_done_callback(_on_completion)
|
||||
|
||||
|
||||
async def staggered_race(
|
||||
coro_fns: Iterable[Callable[[], Awaitable[_T]]],
|
||||
delay: Optional[float],
|
||||
*,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
) -> Tuple[Optional[_T], Optional[int], List[Optional[BaseException]]]:
|
||||
"""
|
||||
Run coroutines with staggered start times and take the first to finish.
|
||||
|
||||
This method takes an iterable of coroutine functions. The first one is
|
||||
started immediately. From then on, whenever the immediately preceding one
|
||||
fails (raises an exception), or when *delay* seconds has passed, the next
|
||||
coroutine is started. This continues until one of the coroutines complete
|
||||
successfully, in which case all others are cancelled, or until all
|
||||
coroutines fail.
|
||||
|
||||
The coroutines provided should be well-behaved in the following way:
|
||||
|
||||
* They should only ``return`` if completed successfully.
|
||||
|
||||
* They should always raise an exception if they did not complete
|
||||
successfully. In particular, if they handle cancellation, they should
|
||||
probably reraise, like this::
|
||||
|
||||
try:
|
||||
# do work
|
||||
except asyncio.CancelledError:
|
||||
# undo partially completed work
|
||||
raise
|
||||
|
||||
Args:
|
||||
----
|
||||
coro_fns: an iterable of coroutine functions, i.e. callables that
|
||||
return a coroutine object when called. Use ``functools.partial`` or
|
||||
lambdas to pass arguments.
|
||||
|
||||
delay: amount of time, in seconds, between starting coroutines. If
|
||||
``None``, the coroutines will run sequentially.
|
||||
|
||||
loop: the event loop to use. If ``None``, the running loop is used.
|
||||
|
||||
Returns:
|
||||
-------
|
||||
tuple *(winner_result, winner_index, exceptions)* where
|
||||
|
||||
- *winner_result*: the result of the winning coroutine, or ``None``
|
||||
if no coroutines won.
|
||||
|
||||
- *winner_index*: the index of the winning coroutine in
|
||||
``coro_fns``, or ``None`` if no coroutines won. If the winning
|
||||
coroutine may return None on success, *winner_index* can be used
|
||||
to definitively determine whether any coroutine won.
|
||||
|
||||
- *exceptions*: list of exceptions returned by the coroutines.
|
||||
``len(exceptions)`` is equal to the number of coroutines actually
|
||||
started, and the order is the same as in ``coro_fns``. The winning
|
||||
coroutine's entry is ``None``.
|
||||
|
||||
"""
|
||||
loop = loop or asyncio.get_running_loop()
|
||||
exceptions: List[Optional[BaseException]] = []
|
||||
tasks: Set[asyncio.Task[Optional[Tuple[_T, int]]]] = set()
|
||||
|
||||
async def run_one_coro(
|
||||
coro_fn: Callable[[], Awaitable[_T]],
|
||||
this_index: int,
|
||||
start_next: "asyncio.Future[None]",
|
||||
) -> Optional[Tuple[_T, int]]:
|
||||
"""
|
||||
Run a single coroutine.
|
||||
|
||||
If the coroutine fails, set the exception in the exceptions list and
|
||||
start the next coroutine by setting the result of the start_next.
|
||||
|
||||
If the coroutine succeeds, return the result and the index of the
|
||||
coroutine in the coro_fns list.
|
||||
|
||||
If SystemExit or KeyboardInterrupt is raised, re-raise it.
|
||||
"""
|
||||
try:
|
||||
result = await coro_fn()
|
||||
except RE_RAISE_EXCEPTIONS:
|
||||
raise
|
||||
except BaseException as e:
|
||||
exceptions[this_index] = e
|
||||
_set_result(start_next) # Kickstart the next coroutine
|
||||
return None
|
||||
|
||||
return result, this_index
|
||||
|
||||
start_next_timer: Optional[asyncio.TimerHandle] = None
|
||||
start_next: Optional[asyncio.Future[None]]
|
||||
task: asyncio.Task[Optional[Tuple[_T, int]]]
|
||||
done: Union[asyncio.Future[None], asyncio.Task[Optional[Tuple[_T, int]]]]
|
||||
coro_iter = iter(coro_fns)
|
||||
this_index = -1
|
||||
try:
|
||||
while True:
|
||||
if coro_fn := next(coro_iter, None):
|
||||
this_index += 1
|
||||
exceptions.append(None)
|
||||
start_next = loop.create_future()
|
||||
task = loop.create_task(run_one_coro(coro_fn, this_index, start_next))
|
||||
tasks.add(task)
|
||||
start_next_timer = (
|
||||
loop.call_later(delay, _set_result, start_next) if delay else None
|
||||
)
|
||||
elif not tasks:
|
||||
# We exhausted the coro_fns list and no tasks are running
|
||||
# so we have no winner and all coroutines failed.
|
||||
break
|
||||
|
||||
while tasks or start_next:
|
||||
done = await _wait_one(
|
||||
(*tasks, start_next) if start_next else tasks, loop
|
||||
)
|
||||
if done is start_next:
|
||||
# The current task has failed or the timer has expired
|
||||
# so we need to start the next task.
|
||||
start_next = None
|
||||
if start_next_timer:
|
||||
start_next_timer.cancel()
|
||||
start_next_timer = None
|
||||
|
||||
# Break out of the task waiting loop to start the next
|
||||
# task.
|
||||
break
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(done, asyncio.Task)
|
||||
|
||||
tasks.remove(done)
|
||||
if winner := done.result():
|
||||
return *winner, exceptions
|
||||
finally:
|
||||
# We either have:
|
||||
# - a winner
|
||||
# - all tasks failed
|
||||
# - a KeyboardInterrupt or SystemExit.
|
||||
|
||||
#
|
||||
# If the timer is still running, cancel it.
|
||||
#
|
||||
if start_next_timer:
|
||||
start_next_timer.cancel()
|
||||
|
||||
#
|
||||
# If there are any tasks left, cancel them and than
|
||||
# wait them so they fill the exceptions list.
|
||||
#
|
||||
for task in tasks:
|
||||
task.cancel()
|
||||
with contextlib.suppress(asyncio.CancelledError):
|
||||
await task
|
||||
|
||||
return None, None, exceptions
|
||||
259
venv/Lib/site-packages/aiohappyeyeballs/impl.py
Normal file
259
venv/Lib/site-packages/aiohappyeyeballs/impl.py
Normal file
@@ -0,0 +1,259 @@
|
||||
"""Base implementation."""
|
||||
|
||||
import asyncio
|
||||
import collections
|
||||
import contextlib
|
||||
import functools
|
||||
import itertools
|
||||
import socket
|
||||
from typing import List, Optional, Sequence, Set, Union
|
||||
|
||||
from . import _staggered
|
||||
from .types import AddrInfoType, SocketFactoryType
|
||||
|
||||
|
||||
async def start_connection(
|
||||
addr_infos: Sequence[AddrInfoType],
|
||||
*,
|
||||
local_addr_infos: Optional[Sequence[AddrInfoType]] = None,
|
||||
happy_eyeballs_delay: Optional[float] = None,
|
||||
interleave: Optional[int] = None,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
socket_factory: Optional[SocketFactoryType] = None,
|
||||
) -> socket.socket:
|
||||
"""
|
||||
Connect to a TCP server.
|
||||
|
||||
Create a socket connection to a specified destination. The
|
||||
destination is specified as a list of AddrInfoType tuples as
|
||||
returned from getaddrinfo().
|
||||
|
||||
The arguments are, in order:
|
||||
|
||||
* ``family``: the address family, e.g. ``socket.AF_INET`` or
|
||||
``socket.AF_INET6``.
|
||||
* ``type``: the socket type, e.g. ``socket.SOCK_STREAM`` or
|
||||
``socket.SOCK_DGRAM``.
|
||||
* ``proto``: the protocol, e.g. ``socket.IPPROTO_TCP`` or
|
||||
``socket.IPPROTO_UDP``.
|
||||
* ``canonname``: the canonical name of the address, e.g.
|
||||
``"www.python.org"``.
|
||||
* ``sockaddr``: the socket address
|
||||
|
||||
This method is a coroutine which will try to establish the connection
|
||||
in the background. When successful, the coroutine returns a
|
||||
socket.
|
||||
|
||||
The expected use case is to use this method in conjunction with
|
||||
loop.create_connection() to establish a connection to a server::
|
||||
|
||||
socket = await start_connection(addr_infos)
|
||||
transport, protocol = await loop.create_connection(
|
||||
MyProtocol, sock=socket, ...)
|
||||
"""
|
||||
if not (current_loop := loop):
|
||||
current_loop = asyncio.get_running_loop()
|
||||
|
||||
single_addr_info = len(addr_infos) == 1
|
||||
|
||||
if happy_eyeballs_delay is not None and interleave is None:
|
||||
# If using happy eyeballs, default to interleave addresses by family
|
||||
interleave = 1
|
||||
|
||||
if interleave and not single_addr_info:
|
||||
addr_infos = _interleave_addrinfos(addr_infos, interleave)
|
||||
|
||||
sock: Optional[socket.socket] = None
|
||||
# uvloop can raise RuntimeError instead of OSError
|
||||
exceptions: List[List[Union[OSError, RuntimeError]]] = []
|
||||
if happy_eyeballs_delay is None or single_addr_info:
|
||||
# not using happy eyeballs
|
||||
for addrinfo in addr_infos:
|
||||
try:
|
||||
sock = await _connect_sock(
|
||||
current_loop,
|
||||
exceptions,
|
||||
addrinfo,
|
||||
local_addr_infos,
|
||||
None,
|
||||
socket_factory,
|
||||
)
|
||||
break
|
||||
except (RuntimeError, OSError):
|
||||
continue
|
||||
else: # using happy eyeballs
|
||||
open_sockets: Set[socket.socket] = set()
|
||||
try:
|
||||
sock, _, _ = await _staggered.staggered_race(
|
||||
(
|
||||
functools.partial(
|
||||
_connect_sock,
|
||||
current_loop,
|
||||
exceptions,
|
||||
addrinfo,
|
||||
local_addr_infos,
|
||||
open_sockets,
|
||||
socket_factory,
|
||||
)
|
||||
for addrinfo in addr_infos
|
||||
),
|
||||
happy_eyeballs_delay,
|
||||
)
|
||||
finally:
|
||||
# If we have a winner, staggered_race will
|
||||
# cancel the other tasks, however there is a
|
||||
# small race window where any of the other tasks
|
||||
# can be done before they are cancelled which
|
||||
# will leave the socket open. To avoid this problem
|
||||
# we pass a set to _connect_sock to keep track of
|
||||
# the open sockets and close them here if there
|
||||
# are any "runner up" sockets.
|
||||
for s in open_sockets:
|
||||
if s is not sock:
|
||||
with contextlib.suppress(OSError):
|
||||
s.close()
|
||||
open_sockets = None # type: ignore[assignment]
|
||||
|
||||
if sock is None:
|
||||
all_exceptions = [exc for sub in exceptions for exc in sub]
|
||||
try:
|
||||
first_exception = all_exceptions[0]
|
||||
if len(all_exceptions) == 1:
|
||||
raise first_exception
|
||||
else:
|
||||
# If they all have the same str(), raise one.
|
||||
model = str(first_exception)
|
||||
if all(str(exc) == model for exc in all_exceptions):
|
||||
raise first_exception
|
||||
# Raise a combined exception so the user can see all
|
||||
# the various error messages.
|
||||
msg = "Multiple exceptions: {}".format(
|
||||
", ".join(str(exc) for exc in all_exceptions)
|
||||
)
|
||||
# If the errno is the same for all exceptions, raise
|
||||
# an OSError with that errno.
|
||||
if isinstance(first_exception, OSError):
|
||||
first_errno = first_exception.errno
|
||||
if all(
|
||||
isinstance(exc, OSError) and exc.errno == first_errno
|
||||
for exc in all_exceptions
|
||||
):
|
||||
raise OSError(first_errno, msg)
|
||||
elif isinstance(first_exception, RuntimeError) and all(
|
||||
isinstance(exc, RuntimeError) for exc in all_exceptions
|
||||
):
|
||||
raise RuntimeError(msg)
|
||||
# We have a mix of OSError and RuntimeError
|
||||
# so we have to pick which one to raise.
|
||||
# and we raise OSError for compatibility
|
||||
raise OSError(msg)
|
||||
finally:
|
||||
all_exceptions = None # type: ignore[assignment]
|
||||
exceptions = None # type: ignore[assignment]
|
||||
|
||||
return sock
|
||||
|
||||
|
||||
async def _connect_sock(
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
exceptions: List[List[Union[OSError, RuntimeError]]],
|
||||
addr_info: AddrInfoType,
|
||||
local_addr_infos: Optional[Sequence[AddrInfoType]] = None,
|
||||
open_sockets: Optional[Set[socket.socket]] = None,
|
||||
socket_factory: Optional[SocketFactoryType] = None,
|
||||
) -> socket.socket:
|
||||
"""
|
||||
Create, bind and connect one socket.
|
||||
|
||||
If open_sockets is passed, add the socket to the set of open sockets.
|
||||
Any failure caught here will remove the socket from the set and close it.
|
||||
|
||||
Callers can use this set to close any sockets that are not the winner
|
||||
of all staggered tasks in the result there are runner up sockets aka
|
||||
multiple winners.
|
||||
"""
|
||||
my_exceptions: List[Union[OSError, RuntimeError]] = []
|
||||
exceptions.append(my_exceptions)
|
||||
family, type_, proto, _, address = addr_info
|
||||
sock = None
|
||||
try:
|
||||
if socket_factory is not None:
|
||||
sock = socket_factory(addr_info)
|
||||
else:
|
||||
sock = socket.socket(family=family, type=type_, proto=proto)
|
||||
if open_sockets is not None:
|
||||
open_sockets.add(sock)
|
||||
sock.setblocking(False)
|
||||
if local_addr_infos is not None:
|
||||
for lfamily, _, _, _, laddr in local_addr_infos:
|
||||
# skip local addresses of different family
|
||||
if lfamily != family:
|
||||
continue
|
||||
try:
|
||||
sock.bind(laddr)
|
||||
break
|
||||
except OSError as exc:
|
||||
msg = (
|
||||
f"error while attempting to bind on "
|
||||
f"address {laddr!r}: "
|
||||
f"{(exc.strerror or '').lower()}"
|
||||
)
|
||||
exc = OSError(exc.errno, msg)
|
||||
my_exceptions.append(exc)
|
||||
else: # all bind attempts failed
|
||||
if my_exceptions:
|
||||
raise my_exceptions.pop()
|
||||
else:
|
||||
raise OSError(f"no matching local address with {family=} found")
|
||||
await loop.sock_connect(sock, address)
|
||||
return sock
|
||||
except (RuntimeError, OSError) as exc:
|
||||
my_exceptions.append(exc)
|
||||
if sock is not None:
|
||||
if open_sockets is not None:
|
||||
open_sockets.remove(sock)
|
||||
try:
|
||||
sock.close()
|
||||
except OSError as e:
|
||||
my_exceptions.append(e)
|
||||
raise
|
||||
raise
|
||||
except:
|
||||
if sock is not None:
|
||||
if open_sockets is not None:
|
||||
open_sockets.remove(sock)
|
||||
try:
|
||||
sock.close()
|
||||
except OSError as e:
|
||||
my_exceptions.append(e)
|
||||
raise
|
||||
raise
|
||||
finally:
|
||||
exceptions = my_exceptions = None # type: ignore[assignment]
|
||||
|
||||
|
||||
def _interleave_addrinfos(
|
||||
addrinfos: Sequence[AddrInfoType], first_address_family_count: int = 1
|
||||
) -> List[AddrInfoType]:
|
||||
"""Interleave list of addrinfo tuples by family."""
|
||||
# Group addresses by family
|
||||
addrinfos_by_family: collections.OrderedDict[int, List[AddrInfoType]] = (
|
||||
collections.OrderedDict()
|
||||
)
|
||||
for addr in addrinfos:
|
||||
family = addr[0]
|
||||
if family not in addrinfos_by_family:
|
||||
addrinfos_by_family[family] = []
|
||||
addrinfos_by_family[family].append(addr)
|
||||
addrinfos_lists = list(addrinfos_by_family.values())
|
||||
|
||||
reordered: List[AddrInfoType] = []
|
||||
if first_address_family_count > 1:
|
||||
reordered.extend(addrinfos_lists[0][: first_address_family_count - 1])
|
||||
del addrinfos_lists[0][: first_address_family_count - 1]
|
||||
reordered.extend(
|
||||
a
|
||||
for a in itertools.chain.from_iterable(itertools.zip_longest(*addrinfos_lists))
|
||||
if a is not None
|
||||
)
|
||||
return reordered
|
||||
0
venv/Lib/site-packages/aiohappyeyeballs/py.typed
Normal file
0
venv/Lib/site-packages/aiohappyeyeballs/py.typed
Normal file
17
venv/Lib/site-packages/aiohappyeyeballs/types.py
Normal file
17
venv/Lib/site-packages/aiohappyeyeballs/types.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Types for aiohappyeyeballs."""
|
||||
|
||||
import socket
|
||||
|
||||
# PY3.9: Import Callable from typing until we drop Python 3.9 support
|
||||
# https://github.com/python/cpython/issues/87131
|
||||
from typing import Callable, Tuple, Union
|
||||
|
||||
AddrInfoType = Tuple[
|
||||
Union[int, socket.AddressFamily],
|
||||
Union[int, socket.SocketKind],
|
||||
int,
|
||||
str,
|
||||
Tuple, # type: ignore[type-arg]
|
||||
]
|
||||
|
||||
SocketFactoryType = Callable[[AddrInfoType], socket.socket]
|
||||
97
venv/Lib/site-packages/aiohappyeyeballs/utils.py
Normal file
97
venv/Lib/site-packages/aiohappyeyeballs/utils.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""Utility functions for aiohappyeyeballs."""
|
||||
|
||||
import ipaddress
|
||||
import socket
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
|
||||
from .types import AddrInfoType
|
||||
|
||||
|
||||
def addr_to_addr_infos(
|
||||
addr: Optional[
|
||||
Union[Tuple[str, int, int, int], Tuple[str, int, int], Tuple[str, int]]
|
||||
],
|
||||
) -> Optional[List[AddrInfoType]]:
|
||||
"""Convert an address tuple to a list of addr_info tuples."""
|
||||
if addr is None:
|
||||
return None
|
||||
host = addr[0]
|
||||
port = addr[1]
|
||||
is_ipv6 = ":" in host
|
||||
if is_ipv6:
|
||||
flowinfo = 0
|
||||
scopeid = 0
|
||||
addr_len = len(addr)
|
||||
if addr_len >= 4:
|
||||
scopeid = addr[3] # type: ignore[misc]
|
||||
if addr_len >= 3:
|
||||
flowinfo = addr[2] # type: ignore[misc]
|
||||
addr = (host, port, flowinfo, scopeid)
|
||||
family = socket.AF_INET6
|
||||
else:
|
||||
addr = (host, port)
|
||||
family = socket.AF_INET
|
||||
return [(family, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", addr)]
|
||||
|
||||
|
||||
def pop_addr_infos_interleave(
|
||||
addr_infos: List[AddrInfoType], interleave: Optional[int] = None
|
||||
) -> None:
|
||||
"""
|
||||
Pop addr_info from the list of addr_infos by family up to interleave times.
|
||||
|
||||
The interleave parameter is used to know how many addr_infos for
|
||||
each family should be popped of the top of the list.
|
||||
"""
|
||||
seen: Dict[int, int] = {}
|
||||
if interleave is None:
|
||||
interleave = 1
|
||||
to_remove: List[AddrInfoType] = []
|
||||
for addr_info in addr_infos:
|
||||
family = addr_info[0]
|
||||
if family not in seen:
|
||||
seen[family] = 0
|
||||
if seen[family] < interleave:
|
||||
to_remove.append(addr_info)
|
||||
seen[family] += 1
|
||||
for addr_info in to_remove:
|
||||
addr_infos.remove(addr_info)
|
||||
|
||||
|
||||
def _addr_tuple_to_ip_address(
|
||||
addr: Union[Tuple[str, int], Tuple[str, int, int, int]],
|
||||
) -> Union[
|
||||
Tuple[ipaddress.IPv4Address, int], Tuple[ipaddress.IPv6Address, int, int, int]
|
||||
]:
|
||||
"""Convert an address tuple to an IPv4Address."""
|
||||
return (ipaddress.ip_address(addr[0]), *addr[1:])
|
||||
|
||||
|
||||
def remove_addr_infos(
|
||||
addr_infos: List[AddrInfoType],
|
||||
addr: Union[Tuple[str, int], Tuple[str, int, int, int]],
|
||||
) -> None:
|
||||
"""
|
||||
Remove an address from the list of addr_infos.
|
||||
|
||||
The addr value is typically the return value of
|
||||
sock.getpeername().
|
||||
"""
|
||||
bad_addrs_infos: List[AddrInfoType] = []
|
||||
for addr_info in addr_infos:
|
||||
if addr_info[-1] == addr:
|
||||
bad_addrs_infos.append(addr_info)
|
||||
if bad_addrs_infos:
|
||||
for bad_addr_info in bad_addrs_infos:
|
||||
addr_infos.remove(bad_addr_info)
|
||||
return
|
||||
# Slow path in case addr is formatted differently
|
||||
match_addr = _addr_tuple_to_ip_address(addr)
|
||||
for addr_info in addr_infos:
|
||||
if match_addr == _addr_tuple_to_ip_address(addr_info[-1]):
|
||||
bad_addrs_infos.append(addr_info)
|
||||
if bad_addrs_infos:
|
||||
for bad_addr_info in bad_addrs_infos:
|
||||
addr_infos.remove(bad_addr_info)
|
||||
return
|
||||
raise ValueError(f"Address {addr} not found in addr_infos")
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
262
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/METADATA
Normal file
262
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/METADATA
Normal file
@@ -0,0 +1,262 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: aiohttp
|
||||
Version: 3.13.3
|
||||
Summary: Async http client/server framework (asyncio)
|
||||
Maintainer-email: aiohttp team <team@aiohttp.org>
|
||||
License: Apache-2.0 AND MIT
|
||||
Project-URL: Homepage, https://github.com/aio-libs/aiohttp
|
||||
Project-URL: Chat: Matrix, https://matrix.to/#/#aio-libs:matrix.org
|
||||
Project-URL: Chat: Matrix Space, https://matrix.to/#/#aio-libs-space:matrix.org
|
||||
Project-URL: CI: GitHub Actions, https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI
|
||||
Project-URL: Coverage: codecov, https://codecov.io/github/aio-libs/aiohttp
|
||||
Project-URL: Docs: Changelog, https://docs.aiohttp.org/en/stable/changes.html
|
||||
Project-URL: Docs: RTD, https://docs.aiohttp.org
|
||||
Project-URL: GitHub: issues, https://github.com/aio-libs/aiohttp/issues
|
||||
Project-URL: GitHub: repo, https://github.com/aio-libs/aiohttp
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Framework :: AsyncIO
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Operating System :: POSIX
|
||||
Classifier: Operating System :: MacOS :: MacOS X
|
||||
Classifier: Operating System :: Microsoft :: Windows
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: Programming Language :: Python :: 3.14
|
||||
Classifier: Topic :: Internet :: WWW/HTTP
|
||||
Requires-Python: >=3.9
|
||||
Description-Content-Type: text/x-rst
|
||||
License-File: LICENSE.txt
|
||||
License-File: vendor/llhttp/LICENSE
|
||||
Requires-Dist: aiohappyeyeballs>=2.5.0
|
||||
Requires-Dist: aiosignal>=1.4.0
|
||||
Requires-Dist: async-timeout<6.0,>=4.0; python_version < "3.11"
|
||||
Requires-Dist: attrs>=17.3.0
|
||||
Requires-Dist: frozenlist>=1.1.1
|
||||
Requires-Dist: multidict<7.0,>=4.5
|
||||
Requires-Dist: propcache>=0.2.0
|
||||
Requires-Dist: yarl<2.0,>=1.17.0
|
||||
Provides-Extra: speedups
|
||||
Requires-Dist: aiodns>=3.3.0; extra == "speedups"
|
||||
Requires-Dist: Brotli>=1.2; platform_python_implementation == "CPython" and extra == "speedups"
|
||||
Requires-Dist: brotlicffi>=1.2; platform_python_implementation != "CPython" and extra == "speedups"
|
||||
Requires-Dist: backports.zstd; (platform_python_implementation == "CPython" and python_version < "3.14") and extra == "speedups"
|
||||
Dynamic: license-file
|
||||
|
||||
==================================
|
||||
Async http client/server framework
|
||||
==================================
|
||||
|
||||
.. image:: https://raw.githubusercontent.com/aio-libs/aiohttp/master/docs/aiohttp-plain.svg
|
||||
:height: 64px
|
||||
:width: 64px
|
||||
:alt: aiohttp logo
|
||||
|
||||
|
|
||||
|
||||
.. image:: https://github.com/aio-libs/aiohttp/workflows/CI/badge.svg
|
||||
:target: https://github.com/aio-libs/aiohttp/actions?query=workflow%3ACI
|
||||
:alt: GitHub Actions status for master branch
|
||||
|
||||
.. image:: https://codecov.io/gh/aio-libs/aiohttp/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/aio-libs/aiohttp
|
||||
:alt: codecov.io status for master branch
|
||||
|
||||
.. image:: https://badge.fury.io/py/aiohttp.svg
|
||||
:target: https://pypi.org/project/aiohttp
|
||||
:alt: Latest PyPI package version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/aiohttp
|
||||
:target: https://pypistats.org/packages/aiohttp
|
||||
:alt: Downloads count
|
||||
|
||||
.. image:: https://readthedocs.org/projects/aiohttp/badge/?version=latest
|
||||
:target: https://docs.aiohttp.org/
|
||||
:alt: Latest Read The Docs
|
||||
|
||||
.. image:: https://img.shields.io/endpoint?url=https://codspeed.io/badge.json
|
||||
:target: https://codspeed.io/aio-libs/aiohttp
|
||||
:alt: Codspeed.io status for aiohttp
|
||||
|
||||
|
||||
Key Features
|
||||
============
|
||||
|
||||
- Supports both client and server side of HTTP protocol.
|
||||
- Supports both client and server Web-Sockets out-of-the-box and avoids
|
||||
Callback Hell.
|
||||
- Provides Web-server with middleware and pluggable routing.
|
||||
|
||||
|
||||
Getting started
|
||||
===============
|
||||
|
||||
Client
|
||||
------
|
||||
|
||||
To get something from the web:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import aiohttp
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get('http://python.org') as response:
|
||||
|
||||
print("Status:", response.status)
|
||||
print("Content-type:", response.headers['content-type'])
|
||||
|
||||
html = await response.text()
|
||||
print("Body:", html[:15], "...")
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
This prints:
|
||||
|
||||
.. code-block::
|
||||
|
||||
Status: 200
|
||||
Content-type: text/html; charset=utf-8
|
||||
Body: <!doctype html> ...
|
||||
|
||||
Coming from `requests <https://requests.readthedocs.io/>`_ ? Read `why we need so many lines <https://aiohttp.readthedocs.io/en/latest/http_request_lifecycle.html>`_.
|
||||
|
||||
Server
|
||||
------
|
||||
|
||||
An example using a simple server:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# examples/server_simple.py
|
||||
from aiohttp import web
|
||||
|
||||
async def handle(request):
|
||||
name = request.match_info.get('name', "Anonymous")
|
||||
text = "Hello, " + name
|
||||
return web.Response(text=text)
|
||||
|
||||
async def wshandle(request):
|
||||
ws = web.WebSocketResponse()
|
||||
await ws.prepare(request)
|
||||
|
||||
async for msg in ws:
|
||||
if msg.type == web.WSMsgType.text:
|
||||
await ws.send_str("Hello, {}".format(msg.data))
|
||||
elif msg.type == web.WSMsgType.binary:
|
||||
await ws.send_bytes(msg.data)
|
||||
elif msg.type == web.WSMsgType.close:
|
||||
break
|
||||
|
||||
return ws
|
||||
|
||||
|
||||
app = web.Application()
|
||||
app.add_routes([web.get('/', handle),
|
||||
web.get('/echo', wshandle),
|
||||
web.get('/{name}', handle)])
|
||||
|
||||
if __name__ == '__main__':
|
||||
web.run_app(app)
|
||||
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
https://aiohttp.readthedocs.io/
|
||||
|
||||
|
||||
Demos
|
||||
=====
|
||||
|
||||
https://github.com/aio-libs/aiohttp-demos
|
||||
|
||||
|
||||
External links
|
||||
==============
|
||||
|
||||
* `Third party libraries
|
||||
<http://aiohttp.readthedocs.io/en/latest/third_party.html>`_
|
||||
* `Built with aiohttp
|
||||
<http://aiohttp.readthedocs.io/en/latest/built_with.html>`_
|
||||
* `Powered by aiohttp
|
||||
<http://aiohttp.readthedocs.io/en/latest/powered_by.html>`_
|
||||
|
||||
Feel free to make a Pull Request for adding your link to these pages!
|
||||
|
||||
|
||||
Communication channels
|
||||
======================
|
||||
|
||||
*aio-libs Discussions*: https://github.com/aio-libs/aiohttp/discussions
|
||||
|
||||
*Matrix*: `#aio-libs:matrix.org <https://matrix.to/#/#aio-libs:matrix.org>`_
|
||||
|
||||
We support `Stack Overflow
|
||||
<https://stackoverflow.com/questions/tagged/aiohttp>`_.
|
||||
Please add *aiohttp* tag to your question there.
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
- attrs_
|
||||
- multidict_
|
||||
- yarl_
|
||||
- frozenlist_
|
||||
|
||||
Optionally you may install the aiodns_ library (highly recommended for sake of speed).
|
||||
|
||||
.. _aiodns: https://pypi.python.org/pypi/aiodns
|
||||
.. _attrs: https://github.com/python-attrs/attrs
|
||||
.. _multidict: https://pypi.python.org/pypi/multidict
|
||||
.. _frozenlist: https://pypi.org/project/frozenlist/
|
||||
.. _yarl: https://pypi.python.org/pypi/yarl
|
||||
.. _async-timeout: https://pypi.python.org/pypi/async_timeout
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
``aiohttp`` is offered under the Apache 2 license.
|
||||
|
||||
|
||||
Keepsafe
|
||||
========
|
||||
|
||||
The aiohttp community would like to thank Keepsafe
|
||||
(https://www.getkeepsafe.com) for its support in the early days of
|
||||
the project.
|
||||
|
||||
|
||||
Source code
|
||||
===========
|
||||
|
||||
The latest developer version is available in a GitHub repository:
|
||||
https://github.com/aio-libs/aiohttp
|
||||
|
||||
Benchmarks
|
||||
==========
|
||||
|
||||
If you are interested in efficiency, the AsyncIO community maintains a
|
||||
list of benchmarks on the official wiki:
|
||||
https://github.com/python/asyncio/wiki/Benchmarks
|
||||
|
||||
--------
|
||||
|
||||
.. image:: https://img.shields.io/matrix/aio-libs:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat
|
||||
:target: https://matrix.to/#/%23aio-libs:matrix.org
|
||||
:alt: Matrix Room — #aio-libs:matrix.org
|
||||
|
||||
.. image:: https://img.shields.io/matrix/aio-libs-space:matrix.org?label=Discuss%20on%20Matrix%20at%20%23aio-libs-space%3Amatrix.org&logo=matrix&server_fqdn=matrix.org&style=flat
|
||||
:target: https://matrix.to/#/%23aio-libs-space:matrix.org
|
||||
:alt: Matrix Space — #aio-libs-space:matrix.org
|
||||
|
||||
.. image:: https://insights.linuxfoundation.org/api/badge/health-score?project=aiohttp
|
||||
:target: https://insights.linuxfoundation.org/project/aiohttp
|
||||
:alt: LFX Health Score
|
||||
138
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/RECORD
Normal file
138
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/RECORD
Normal file
@@ -0,0 +1,138 @@
|
||||
aiohttp-3.13.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
aiohttp-3.13.3.dist-info/METADATA,sha256=jkzui8KtHZ32gb8TfFZwIW4-zZ6Sr1eh1R6wYZW79Sg,8407
|
||||
aiohttp-3.13.3.dist-info/RECORD,,
|
||||
aiohttp-3.13.3.dist-info/WHEEL,sha256=JLOMsP7F5qtkAkINx5UnzbFguf8CqZeraV8o04b0I8I,101
|
||||
aiohttp-3.13.3.dist-info/licenses/LICENSE.txt,sha256=wUk-nxDVnR-6n53ygAjhVX4zz5-6yM4SY6ozk5goA94,601
|
||||
aiohttp-3.13.3.dist-info/licenses/vendor/llhttp/LICENSE,sha256=bd-mKNt20th7iWi6-61g9RxOyIEA3Xu5b5chbYivCAg,1127
|
||||
aiohttp-3.13.3.dist-info/top_level.txt,sha256=iv-JIaacmTl-hSho3QmphcKnbRRYx1st47yjz_178Ro,8
|
||||
aiohttp/.hash/_cparser.pxd.hash,sha256=eJQ2z7M7WoAng7D5ukCXzE3Yx22bLgv1PyOe0YbbQTM,108
|
||||
aiohttp/.hash/_find_header.pxd.hash,sha256=TxG5w4etbVd6sfm5JWbdf5PW6LnuXRQnlMoFBVGKN2E,112
|
||||
aiohttp/.hash/_http_parser.pyx.hash,sha256=NYbk_8ETW0vAtpTcxRVuWVmKJr9CUh2fR8I9emVQck4,112
|
||||
aiohttp/.hash/_http_writer.pyx.hash,sha256=J4W44iDZQwIyZ0rGO5v-_sKIfPtAwqn99EwgaevQmo8,112
|
||||
aiohttp/.hash/hdrs.py.hash,sha256=c2N-IMHz4dvAGL36CUyEw15noHE2AkJTeSBy3IxcCec,103
|
||||
aiohttp/__init__.py,sha256=wTWxnyVGn59VuoFuK1m2_jJ-Cw5Be9ktp7h5Hfvyaas,8580
|
||||
aiohttp/__pycache__/__init__.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/_cookie_helpers.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/abc.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/base_protocol.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/client.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/client_exceptions.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/client_middleware_digest_auth.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/client_middlewares.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/client_proto.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/client_reqrep.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/client_ws.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/compression_utils.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/connector.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/cookiejar.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/formdata.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/hdrs.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/helpers.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/http.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/http_exceptions.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/http_parser.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/http_websocket.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/http_writer.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/log.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/multipart.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/payload.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/payload_streamer.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/pytest_plugin.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/resolver.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/streams.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/tcp_helpers.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/test_utils.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/tracing.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/typedefs.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_app.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_exceptions.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_fileresponse.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_log.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_middlewares.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_protocol.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_request.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_response.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_routedef.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_runner.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_server.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_urldispatcher.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/web_ws.cpython-311.pyc,,
|
||||
aiohttp/__pycache__/worker.cpython-311.pyc,,
|
||||
aiohttp/_cookie_helpers.py,sha256=x6tVKd6fgqjIFQzQ_z-t_CRl-Pnar7qJh8HUwroSKIA,13997
|
||||
aiohttp/_cparser.pxd,sha256=GP0Y9NqZYQGkJtS81XDzU70e7rRMb34TR7yGMmx5_zs,4453
|
||||
aiohttp/_find_header.pxd,sha256=BFUSmxhemBtblqxzjzH3x03FfxaWlTyuAIOz8YZ5_nM,70
|
||||
aiohttp/_headers.pxi,sha256=1MhCe6Un_KI1tpO85HnDfzVO94BhcirLanAOys5FIHA,2090
|
||||
aiohttp/_http_parser.cp311-win_amd64.pyd,sha256=dT4gnGJDvQ0KtOLOAXOm6DpJQF_0BwMbyosvsZlLffc,246272
|
||||
aiohttp/_http_parser.pyx,sha256=9-jyYF9-4i7ToMV0mvVgQ_rqNa8KGJfhQVY0GGrZuGg,29096
|
||||
aiohttp/_http_writer.cp311-win_amd64.pyd,sha256=6mq-oxVxMiDKUbvJSbu_Mr8YVhD0Nr-3vzlEhz0Rigc,46080
|
||||
aiohttp/_http_writer.pyx,sha256=WWdOf19QPqScBkifDhJynqPPOAmwB9sKJAO0Kkor4tE,4826
|
||||
aiohttp/_websocket/.hash/mask.pxd.hash,sha256=TL0gGYyJWxqG8dWwa08B74WGg6-0M6_Breqrff-AiZg,115
|
||||
aiohttp/_websocket/.hash/mask.pyx.hash,sha256=7xo6f01JaOQmaUNij3dQlOgxkEC1edkAIhwpeOvimLI,115
|
||||
aiohttp/_websocket/.hash/reader_c.pxd.hash,sha256=RzhqjHN1HadWDeMHVQvaf-XLlGxF6nm5u-HJHGsx2aE,119
|
||||
aiohttp/_websocket/__init__.py,sha256=R51KWH5kkdtDLb7T-ilztksbfweKCy3t22SgxGtiY-4,45
|
||||
aiohttp/_websocket/__pycache__/__init__.cpython-311.pyc,,
|
||||
aiohttp/_websocket/__pycache__/helpers.cpython-311.pyc,,
|
||||
aiohttp/_websocket/__pycache__/models.cpython-311.pyc,,
|
||||
aiohttp/_websocket/__pycache__/reader.cpython-311.pyc,,
|
||||
aiohttp/_websocket/__pycache__/reader_c.cpython-311.pyc,,
|
||||
aiohttp/_websocket/__pycache__/reader_py.cpython-311.pyc,,
|
||||
aiohttp/_websocket/__pycache__/writer.cpython-311.pyc,,
|
||||
aiohttp/_websocket/helpers.py,sha256=amqvDhoAKAi8ptB4qUNuQhkaOn-4JxSh_VLAqytmEfw,5185
|
||||
aiohttp/_websocket/mask.cp311-win_amd64.pyd,sha256=OS4oeawxeMRTuZO7uy2QGGQLGMrBAToOsN212alcC5s,36352
|
||||
aiohttp/_websocket/mask.pxd,sha256=41TdSZvhcbYSW_Vrw7bF4r_yoor2njtdaZ3bmvK6-jw,115
|
||||
aiohttp/_websocket/mask.pyx,sha256=Ro7dOOv43HAAqNMz3xyCA11ppcn-vARIvjycStTEYww,1445
|
||||
aiohttp/_websocket/models.py,sha256=Pz8qvnU43VUCNZcY4g03VwTsHOsb_jSN8iG69xMAc_A,2205
|
||||
aiohttp/_websocket/reader.py,sha256=1r0cJ-jdFgbSrC6-jI0zjEA1CppzoUn8u_wiebrVVO0,1061
|
||||
aiohttp/_websocket/reader_c.cp311-win_amd64.pyd,sha256=EwCfwbD6cV0QHtbOOKdgfzhnt8D9XDz1GaFWKNH3ebA,146432
|
||||
aiohttp/_websocket/reader_c.pxd,sha256=HNOl4gRWtNBNEYNbK9PGOfFEQwUqJGexBbDKB_20sl0,2735
|
||||
aiohttp/_websocket/reader_c.py,sha256=UKfslJuANla_CQMe7yIJzE8vp7bpzz9TLr-lH87XW6U,19346
|
||||
aiohttp/_websocket/reader_py.py,sha256=UKfslJuANla_CQMe7yIJzE8vp7bpzz9TLr-lH87XW6U,19346
|
||||
aiohttp/_websocket/writer.py,sha256=MpuNvG_t34CaDTAzW5FZJaRME8sL19rZotxSbXz2aas,11523
|
||||
aiohttp/abc.py,sha256=01N6Y63o2bBC8Vi0ZjO6Jw0V9kXZfy3egwzKFW-tv9c,7417
|
||||
aiohttp/base_protocol.py,sha256=8vNIv6QV_SDCW-8tfhlyxSwiBD7dAiMTqJI1GI8RG5s,3125
|
||||
aiohttp/client.py,sha256=KlWhIZt935YpOZcXOOZl3eIRkuO-l0z2BH7arfhGg-A,59992
|
||||
aiohttp/client_exceptions.py,sha256=sJcuvYKaB2nwuSdP7k18y3wc74aU0xAzdJikzzesrPE,11788
|
||||
aiohttp/client_middleware_digest_auth.py,sha256=K4TPt4-rPQ0jjSHx3UFguMN7n31LpCC_o6JA-Hrg_Pc,18107
|
||||
aiohttp/client_middlewares.py,sha256=FEVIXFkQ58n5bhK4BGEqqDCWnDh-GNJmWq20I5Yt6SU,1973
|
||||
aiohttp/client_proto.py,sha256=rfbg8nUsfpCMM_zGpQygiFn8nzSdBI-731rmXVGHwLc,12469
|
||||
aiohttp/client_reqrep.py,sha256=BUrqo2BJbrNazrIJr-ZgMLRTvE2fSON3zPQSq1dfgfU,54927
|
||||
aiohttp/client_ws.py,sha256=9DraHuupuJcT7NOgyeGml8SBr7V5D5ID5-piY1fQMdA,15537
|
||||
aiohttp/compression_utils.py,sha256=w0ECGGLVjtCXdYg-U_9DBn-DASzDPaWEVRx1HlwWslk,12086
|
||||
aiohttp/connector.py,sha256=X2sRe6EAeWiaP6eaK9hWvLtSbdiJfNhK3bWl7XbR_V4,70846
|
||||
aiohttp/cookiejar.py,sha256=C2fVzQGFieFP9mFDTOvfEc6fb5kPS2ijL2tFKAUW7Sw,19444
|
||||
aiohttp/formdata.py,sha256=sz3VaTHVk11z_5G1LaDhUwrONJ8zRAGlZGg3hcCApzA,6563
|
||||
aiohttp/hdrs.py,sha256=7htmhgZyE9HqWbPpxHU0r7kAIdT2kpOXQa1AadDh2W8,5232
|
||||
aiohttp/helpers.py,sha256=1tXIvGSRWJD9wsS7GUVHLfJEsDM_XigurpgjxajkH0g,31615
|
||||
aiohttp/http.py,sha256=DGKcwDbgIMpasv7s2jeKCRuixyj7W-RIrihRFjj0xcY,1914
|
||||
aiohttp/http_exceptions.py,sha256=J3v-1S9S22GfAEtx0pEqp6d4G1Lqi2-gOrdLtuGlEhY,3185
|
||||
aiohttp/http_parser.py,sha256=O5ud4wO80WLFe9kpXU0xGhjczUfrb7BAr0XAP7rBn7E,39263
|
||||
aiohttp/http_websocket.py,sha256=b9kBmxPLPFQP_nu_sMhIMIeqDOm0ug8G4prbrhEMHZ0,878
|
||||
aiohttp/http_writer.py,sha256=jA_aJW7JdH1mihrIYdJcLOHVKQ4Agg3g993v50eITBs,12824
|
||||
aiohttp/log.py,sha256=zYUTvXsMQ9Sz1yNN8kXwd5Qxu49a1FzjZ_wQqriEc8M,333
|
||||
aiohttp/multipart.py,sha256=UvcLOX3lO3ad3nfODsdlyvYWMAZHdUZ-wlZ5w1TbD2E,41634
|
||||
aiohttp/payload.py,sha256=Xbs_2l0wDaThFG-ehNlvzQUkHuBPpc5FxpJnJa3ZPcs,41994
|
||||
aiohttp/payload_streamer.py,sha256=K0iV85iW0vEG3rDkcopruidspynzQvrwW8mJvgPHisg,2289
|
||||
aiohttp/py.typed,sha256=3VVwXUAWVEVX7sDwyYDnW5ZdBC9_Z9AJAFfLCleUW0k,8
|
||||
aiohttp/pytest_plugin.py,sha256=ymhjbYHz2Kf0ZU_4Ly0hAp73dhsgrQIzJDo4Aot3_TI,13345
|
||||
aiohttp/resolver.py,sha256=ePJgZAN5EQY4YuFiuZmVZM6p3UuzJ4qMWM1fu8DJ2Fc,10305
|
||||
aiohttp/streams.py,sha256=J0G4ZJPdRScOPtnaB1ixhQYjLunLk8z70mfN9bc5K_o,24424
|
||||
aiohttp/tcp_helpers.py,sha256=K-hhGh3jd6qCEnHJo8LvFyfJwBjh99UKI7A0aSRVhj4,998
|
||||
aiohttp/test_utils.py,sha256=zFWAb-rPz1fWRUHnrjnfUH7ORlfIgZ2UZbEGe4YTa9I,23790
|
||||
aiohttp/tracing.py,sha256=Kb-N32aMmYqC2Yc82NV6l0mIcavSQst1BHSFj94Apl0,15013
|
||||
aiohttp/typedefs.py,sha256=Sx5v2yUyLu8nbabqtJRWj1M1_uW0IZACu78uYD7LBy0,1726
|
||||
aiohttp/web.py,sha256=BQ96NEuTWikKGN5NnnTHjFLt07GUMWvvn42iFuIS3Mg,18444
|
||||
aiohttp/web_app.py,sha256=WwEEzUg34j81kK2dPFnhlqx_z6nGjnHZDweZJF65pKc,20072
|
||||
aiohttp/web_exceptions.py,sha256=itNRhCMDJFhnMWftr5SyTsoqh-i0n9rzTj0sjcAEUjo,10812
|
||||
aiohttp/web_fileresponse.py,sha256=QIIbcIruCgfYrc8ZDvOgNlZzLbAagwXA9FrNI7NKNPY,16780
|
||||
aiohttp/web_log.py,sha256=G5ugloW9noUxPft0SmVWOXw30MviL6rqZc3XrKN_T1U,8081
|
||||
aiohttp/web_middlewares.py,sha256=mM2-R8eaV2r6Mi9Zc2bDG8QnhE9h0IzPvtDX_fkKR5s,4286
|
||||
aiohttp/web_protocol.py,sha256=gJaDFtYPA-1gz35fwchjLhxrkmXXMOzFMCDHLQ1FHiI,27802
|
||||
aiohttp/web_request.py,sha256=9zqyP32ScMUylQ_ta4tBHpWmoprhSB4jTgj2ixmGK74,30763
|
||||
aiohttp/web_response.py,sha256=WJVumt-P0uMaFSbef_owvOXpq90E4VMl3RvSOWh0nJE,30197
|
||||
aiohttp/web_routedef.py,sha256=XC10f57Q36JmYaaQqrecsyfIxHMepCKaKkBEB7hLzJI,6324
|
||||
aiohttp/web_runner.py,sha256=zyVYVzCgnopiGwnIhKlNZHtLV_IYQ9aC-Vm43j_HRoA,12185
|
||||
aiohttp/web_server.py,sha256=RZSWt_Mj-Lu89bFYsr_T3rjxW2VNN7PHNJ2mvv2qELs,2972
|
||||
aiohttp/web_urldispatcher.py,sha256=4FiNFUWU_jITYl_DnObptuF5c0ShXAEiWyLVmE-GtN0,45595
|
||||
aiohttp/web_ws.py,sha256=VXHGDtfy_jrBByLvuhnL-A_PmpcoT_ZLyYdj_EcL3Hw,23370
|
||||
aiohttp/worker.py,sha256=N_9iyS_tR9U0pf3BRaIH2nzA1pjN1Xfi2gGmRrMhnho,8407
|
||||
5
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/WHEEL
Normal file
5
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/WHEEL
Normal file
@@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: setuptools (80.9.0)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp311-cp311-win_amd64
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Copyright aio-libs contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
22
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/licenses/vendor/llhttp/LICENSE
vendored
Normal file
22
venv/Lib/site-packages/aiohttp-3.13.3.dist-info/licenses/vendor/llhttp/LICENSE
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
This software is licensed under the MIT License.
|
||||
|
||||
Copyright Fedor Indutny, 2018.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1 @@
|
||||
aiohttp
|
||||
1
venv/Lib/site-packages/aiohttp/.hash/_cparser.pxd.hash
Normal file
1
venv/Lib/site-packages/aiohttp/.hash/_cparser.pxd.hash
Normal file
@@ -0,0 +1 @@
|
||||
18fd18f4da996101a426d4bcd570f353bd1eeeb44c6f7e1347bc86326c79ff3b *D:/a/aiohttp/aiohttp/aiohttp/_cparser.pxd
|
||||
@@ -0,0 +1 @@
|
||||
0455129b185e981b5b96ac738f31f7c74dc57f1696953cae0083b3f18679fe73 *D:/a/aiohttp/aiohttp/aiohttp/_find_header.pxd
|
||||
@@ -0,0 +1 @@
|
||||
f7e8f2605f7ee22ed3a0c5749af56043faea35af0a1897e1415634186ad9b868 *D:/a/aiohttp/aiohttp/aiohttp/_http_parser.pyx
|
||||
@@ -0,0 +1 @@
|
||||
59674e7f5f503ea49c06489f0e12729ea3cf3809b007db0a2403b42a4a2be2d1 *D:/a/aiohttp/aiohttp/aiohttp/_http_writer.pyx
|
||||
1
venv/Lib/site-packages/aiohttp/.hash/hdrs.py.hash
Normal file
1
venv/Lib/site-packages/aiohttp/.hash/hdrs.py.hash
Normal file
@@ -0,0 +1 @@
|
||||
ee1b6686067213d1ea59b3e9c47534afb90021d4f692939741ad4069d0e1d96f *D:/a/aiohttp/aiohttp/aiohttp/hdrs.py
|
||||
278
venv/Lib/site-packages/aiohttp/__init__.py
Normal file
278
venv/Lib/site-packages/aiohttp/__init__.py
Normal file
@@ -0,0 +1,278 @@
|
||||
__version__ = "3.13.3"
|
||||
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
|
||||
from . import hdrs as hdrs
|
||||
from .client import (
|
||||
BaseConnector,
|
||||
ClientConnectionError,
|
||||
ClientConnectionResetError,
|
||||
ClientConnectorCertificateError,
|
||||
ClientConnectorDNSError,
|
||||
ClientConnectorError,
|
||||
ClientConnectorSSLError,
|
||||
ClientError,
|
||||
ClientHttpProxyError,
|
||||
ClientOSError,
|
||||
ClientPayloadError,
|
||||
ClientProxyConnectionError,
|
||||
ClientRequest,
|
||||
ClientResponse,
|
||||
ClientResponseError,
|
||||
ClientSession,
|
||||
ClientSSLError,
|
||||
ClientTimeout,
|
||||
ClientWebSocketResponse,
|
||||
ClientWSTimeout,
|
||||
ConnectionTimeoutError,
|
||||
ContentTypeError,
|
||||
Fingerprint,
|
||||
InvalidURL,
|
||||
InvalidUrlClientError,
|
||||
InvalidUrlRedirectClientError,
|
||||
NamedPipeConnector,
|
||||
NonHttpUrlClientError,
|
||||
NonHttpUrlRedirectClientError,
|
||||
RedirectClientError,
|
||||
RequestInfo,
|
||||
ServerConnectionError,
|
||||
ServerDisconnectedError,
|
||||
ServerFingerprintMismatch,
|
||||
ServerTimeoutError,
|
||||
SocketTimeoutError,
|
||||
TCPConnector,
|
||||
TooManyRedirects,
|
||||
UnixConnector,
|
||||
WSMessageTypeError,
|
||||
WSServerHandshakeError,
|
||||
request,
|
||||
)
|
||||
from .client_middleware_digest_auth import DigestAuthMiddleware
|
||||
from .client_middlewares import ClientHandlerType, ClientMiddlewareType
|
||||
from .compression_utils import set_zlib_backend
|
||||
from .connector import (
|
||||
AddrInfoType as AddrInfoType,
|
||||
SocketFactoryType as SocketFactoryType,
|
||||
)
|
||||
from .cookiejar import CookieJar as CookieJar, DummyCookieJar as DummyCookieJar
|
||||
from .formdata import FormData as FormData
|
||||
from .helpers import BasicAuth, ChainMapProxy, ETag
|
||||
from .http import (
|
||||
HttpVersion as HttpVersion,
|
||||
HttpVersion10 as HttpVersion10,
|
||||
HttpVersion11 as HttpVersion11,
|
||||
WebSocketError as WebSocketError,
|
||||
WSCloseCode as WSCloseCode,
|
||||
WSMessage as WSMessage,
|
||||
WSMsgType as WSMsgType,
|
||||
)
|
||||
from .multipart import (
|
||||
BadContentDispositionHeader as BadContentDispositionHeader,
|
||||
BadContentDispositionParam as BadContentDispositionParam,
|
||||
BodyPartReader as BodyPartReader,
|
||||
MultipartReader as MultipartReader,
|
||||
MultipartWriter as MultipartWriter,
|
||||
content_disposition_filename as content_disposition_filename,
|
||||
parse_content_disposition as parse_content_disposition,
|
||||
)
|
||||
from .payload import (
|
||||
PAYLOAD_REGISTRY as PAYLOAD_REGISTRY,
|
||||
AsyncIterablePayload as AsyncIterablePayload,
|
||||
BufferedReaderPayload as BufferedReaderPayload,
|
||||
BytesIOPayload as BytesIOPayload,
|
||||
BytesPayload as BytesPayload,
|
||||
IOBasePayload as IOBasePayload,
|
||||
JsonPayload as JsonPayload,
|
||||
Payload as Payload,
|
||||
StringIOPayload as StringIOPayload,
|
||||
StringPayload as StringPayload,
|
||||
TextIOPayload as TextIOPayload,
|
||||
get_payload as get_payload,
|
||||
payload_type as payload_type,
|
||||
)
|
||||
from .payload_streamer import streamer as streamer
|
||||
from .resolver import (
|
||||
AsyncResolver as AsyncResolver,
|
||||
DefaultResolver as DefaultResolver,
|
||||
ThreadedResolver as ThreadedResolver,
|
||||
)
|
||||
from .streams import (
|
||||
EMPTY_PAYLOAD as EMPTY_PAYLOAD,
|
||||
DataQueue as DataQueue,
|
||||
EofStream as EofStream,
|
||||
FlowControlDataQueue as FlowControlDataQueue,
|
||||
StreamReader as StreamReader,
|
||||
)
|
||||
from .tracing import (
|
||||
TraceConfig as TraceConfig,
|
||||
TraceConnectionCreateEndParams as TraceConnectionCreateEndParams,
|
||||
TraceConnectionCreateStartParams as TraceConnectionCreateStartParams,
|
||||
TraceConnectionQueuedEndParams as TraceConnectionQueuedEndParams,
|
||||
TraceConnectionQueuedStartParams as TraceConnectionQueuedStartParams,
|
||||
TraceConnectionReuseconnParams as TraceConnectionReuseconnParams,
|
||||
TraceDnsCacheHitParams as TraceDnsCacheHitParams,
|
||||
TraceDnsCacheMissParams as TraceDnsCacheMissParams,
|
||||
TraceDnsResolveHostEndParams as TraceDnsResolveHostEndParams,
|
||||
TraceDnsResolveHostStartParams as TraceDnsResolveHostStartParams,
|
||||
TraceRequestChunkSentParams as TraceRequestChunkSentParams,
|
||||
TraceRequestEndParams as TraceRequestEndParams,
|
||||
TraceRequestExceptionParams as TraceRequestExceptionParams,
|
||||
TraceRequestHeadersSentParams as TraceRequestHeadersSentParams,
|
||||
TraceRequestRedirectParams as TraceRequestRedirectParams,
|
||||
TraceRequestStartParams as TraceRequestStartParams,
|
||||
TraceResponseChunkReceivedParams as TraceResponseChunkReceivedParams,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# At runtime these are lazy-loaded at the bottom of the file.
|
||||
from .worker import (
|
||||
GunicornUVLoopWebWorker as GunicornUVLoopWebWorker,
|
||||
GunicornWebWorker as GunicornWebWorker,
|
||||
)
|
||||
|
||||
__all__: Tuple[str, ...] = (
|
||||
"hdrs",
|
||||
# client
|
||||
"AddrInfoType",
|
||||
"BaseConnector",
|
||||
"ClientConnectionError",
|
||||
"ClientConnectionResetError",
|
||||
"ClientConnectorCertificateError",
|
||||
"ClientConnectorDNSError",
|
||||
"ClientConnectorError",
|
||||
"ClientConnectorSSLError",
|
||||
"ClientError",
|
||||
"ClientHttpProxyError",
|
||||
"ClientOSError",
|
||||
"ClientPayloadError",
|
||||
"ClientProxyConnectionError",
|
||||
"ClientResponse",
|
||||
"ClientRequest",
|
||||
"ClientResponseError",
|
||||
"ClientSSLError",
|
||||
"ClientSession",
|
||||
"ClientTimeout",
|
||||
"ClientWebSocketResponse",
|
||||
"ClientWSTimeout",
|
||||
"ConnectionTimeoutError",
|
||||
"ContentTypeError",
|
||||
"Fingerprint",
|
||||
"FlowControlDataQueue",
|
||||
"InvalidURL",
|
||||
"InvalidUrlClientError",
|
||||
"InvalidUrlRedirectClientError",
|
||||
"NonHttpUrlClientError",
|
||||
"NonHttpUrlRedirectClientError",
|
||||
"RedirectClientError",
|
||||
"RequestInfo",
|
||||
"ServerConnectionError",
|
||||
"ServerDisconnectedError",
|
||||
"ServerFingerprintMismatch",
|
||||
"ServerTimeoutError",
|
||||
"SocketFactoryType",
|
||||
"SocketTimeoutError",
|
||||
"TCPConnector",
|
||||
"TooManyRedirects",
|
||||
"UnixConnector",
|
||||
"NamedPipeConnector",
|
||||
"WSServerHandshakeError",
|
||||
"request",
|
||||
# client_middleware
|
||||
"ClientMiddlewareType",
|
||||
"ClientHandlerType",
|
||||
# cookiejar
|
||||
"CookieJar",
|
||||
"DummyCookieJar",
|
||||
# formdata
|
||||
"FormData",
|
||||
# helpers
|
||||
"BasicAuth",
|
||||
"ChainMapProxy",
|
||||
"DigestAuthMiddleware",
|
||||
"ETag",
|
||||
"set_zlib_backend",
|
||||
# http
|
||||
"HttpVersion",
|
||||
"HttpVersion10",
|
||||
"HttpVersion11",
|
||||
"WSMsgType",
|
||||
"WSCloseCode",
|
||||
"WSMessage",
|
||||
"WebSocketError",
|
||||
# multipart
|
||||
"BadContentDispositionHeader",
|
||||
"BadContentDispositionParam",
|
||||
"BodyPartReader",
|
||||
"MultipartReader",
|
||||
"MultipartWriter",
|
||||
"content_disposition_filename",
|
||||
"parse_content_disposition",
|
||||
# payload
|
||||
"AsyncIterablePayload",
|
||||
"BufferedReaderPayload",
|
||||
"BytesIOPayload",
|
||||
"BytesPayload",
|
||||
"IOBasePayload",
|
||||
"JsonPayload",
|
||||
"PAYLOAD_REGISTRY",
|
||||
"Payload",
|
||||
"StringIOPayload",
|
||||
"StringPayload",
|
||||
"TextIOPayload",
|
||||
"get_payload",
|
||||
"payload_type",
|
||||
# payload_streamer
|
||||
"streamer",
|
||||
# resolver
|
||||
"AsyncResolver",
|
||||
"DefaultResolver",
|
||||
"ThreadedResolver",
|
||||
# streams
|
||||
"DataQueue",
|
||||
"EMPTY_PAYLOAD",
|
||||
"EofStream",
|
||||
"StreamReader",
|
||||
# tracing
|
||||
"TraceConfig",
|
||||
"TraceConnectionCreateEndParams",
|
||||
"TraceConnectionCreateStartParams",
|
||||
"TraceConnectionQueuedEndParams",
|
||||
"TraceConnectionQueuedStartParams",
|
||||
"TraceConnectionReuseconnParams",
|
||||
"TraceDnsCacheHitParams",
|
||||
"TraceDnsCacheMissParams",
|
||||
"TraceDnsResolveHostEndParams",
|
||||
"TraceDnsResolveHostStartParams",
|
||||
"TraceRequestChunkSentParams",
|
||||
"TraceRequestEndParams",
|
||||
"TraceRequestExceptionParams",
|
||||
"TraceRequestHeadersSentParams",
|
||||
"TraceRequestRedirectParams",
|
||||
"TraceRequestStartParams",
|
||||
"TraceResponseChunkReceivedParams",
|
||||
# workers (imported lazily with __getattr__)
|
||||
"GunicornUVLoopWebWorker",
|
||||
"GunicornWebWorker",
|
||||
"WSMessageTypeError",
|
||||
)
|
||||
|
||||
|
||||
def __dir__() -> Tuple[str, ...]:
|
||||
return __all__ + ("__doc__",)
|
||||
|
||||
|
||||
def __getattr__(name: str) -> object:
|
||||
global GunicornUVLoopWebWorker, GunicornWebWorker
|
||||
|
||||
# Importing gunicorn takes a long time (>100ms), so only import if actually needed.
|
||||
if name in ("GunicornUVLoopWebWorker", "GunicornWebWorker"):
|
||||
try:
|
||||
from .worker import GunicornUVLoopWebWorker as guv, GunicornWebWorker as gw
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
GunicornUVLoopWebWorker = guv # type: ignore[misc]
|
||||
GunicornWebWorker = gw # type: ignore[misc]
|
||||
return guv if name == "GunicornUVLoopWebWorker" else gw
|
||||
|
||||
raise AttributeError(f"module {__name__} has no attribute {name}")
|
||||
Binary file not shown.
Binary file not shown.
BIN
venv/Lib/site-packages/aiohttp/__pycache__/abc.cpython-311.pyc
Normal file
BIN
venv/Lib/site-packages/aiohttp/__pycache__/abc.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
venv/Lib/site-packages/aiohttp/__pycache__/hdrs.cpython-311.pyc
Normal file
BIN
venv/Lib/site-packages/aiohttp/__pycache__/hdrs.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
venv/Lib/site-packages/aiohttp/__pycache__/http.cpython-311.pyc
Normal file
BIN
venv/Lib/site-packages/aiohttp/__pycache__/http.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
venv/Lib/site-packages/aiohttp/__pycache__/log.cpython-311.pyc
Normal file
BIN
venv/Lib/site-packages/aiohttp/__pycache__/log.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user