Spaces:
Running
Running
Update app/price_fetcher.py
Browse files- app/price_fetcher.py +15 -45
app/price_fetcher.py
CHANGED
@@ -10,7 +10,12 @@ Features:
|
|
10 |
"""
|
11 |
import asyncio
|
12 |
import logging
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
import httpx
|
16 |
|
@@ -46,7 +51,7 @@ class PriceFetcher:
|
|
46 |
"""
|
47 |
self.client = client
|
48 |
self.coins = coins
|
49 |
-
self._prices: dict[str, float
|
50 |
self._lock = asyncio.Lock() # Lock to prevent race conditions on the cache
|
51 |
self.sources: list[PriceSource] = self._configure_sources()
|
52 |
|
@@ -92,8 +97,13 @@ class PriceFetcher:
|
|
92 |
except (KeyError, TypeError, ValueError) as e:
|
93 |
logging.error("β [CoinCap] Failed to parse response: %s", e)
|
94 |
return {}
|
95 |
-
|
96 |
-
|
|
|
|
|
|
|
|
|
|
|
97 |
"""Returns a copy of the current price cache. Thread-safe read."""
|
98 |
return self._prices.copy()
|
99 |
|
@@ -132,44 +142,4 @@ class PriceFetcher:
|
|
132 |
# Brief pause before trying the next API source
|
133 |
await asyncio.sleep(1)
|
134 |
|
135 |
-
logging.error("β All price APIs failed. Retaining stale prices.")
|
136 |
-
|
137 |
-
|
138 |
-
async def run_price_updates_periodically(fetcher: PriceFetcher, interval_seconds: int):
|
139 |
-
"""A background task runner to keep prices updated."""
|
140 |
-
logging.info("π Starting periodic price updates...")
|
141 |
-
while True:
|
142 |
-
await fetcher.update_prices_async()
|
143 |
-
await asyncio.sleep(interval_seconds)
|
144 |
-
|
145 |
-
|
146 |
-
# --- Example Usage ---
|
147 |
-
if __name__ == "__main__":
|
148 |
-
async def main():
|
149 |
-
"""Demonstrates how to use the PriceFetcher."""
|
150 |
-
target_coins = ["bitcoin", "ethereum", "dogecoin"]
|
151 |
-
|
152 |
-
async with httpx.AsyncClient() as client:
|
153 |
-
price_fetcher = PriceFetcher(client, coins=target_coins)
|
154 |
-
|
155 |
-
# Run the price updates in the background
|
156 |
-
update_task = asyncio.create_task(
|
157 |
-
run_price_updates_periodically(price_fetcher, interval_seconds=10)
|
158 |
-
)
|
159 |
-
|
160 |
-
# In a real app, the server would be running. Here, we just print prices.
|
161 |
-
for i in range(5):
|
162 |
-
await asyncio.sleep(11)
|
163 |
-
current_prices = price_fetcher.get_current_prices()
|
164 |
-
print(f"--- Main App Reading Cache ({i+1}/5) ---")
|
165 |
-
for coin, price in current_prices.items():
|
166 |
-
print(f" {coin.capitalize()}: ${price}")
|
167 |
-
|
168 |
-
# Cleanly shut down the background task
|
169 |
-
update_task.cancel()
|
170 |
-
try:
|
171 |
-
await update_task
|
172 |
-
except asyncio.CancelledError:
|
173 |
-
logging.info("Shutdown complete.")
|
174 |
-
|
175 |
-
asyncio.run(main())
|
|
|
10 |
"""
|
11 |
import asyncio
|
12 |
import logging
|
13 |
+
# ====================================================================
|
14 |
+
# FIX APPLIED HERE (1 of 2)
|
15 |
+
# ====================================================================
|
16 |
+
# Import Union for Python 3.9 compatibility, and other necessary types.
|
17 |
+
from typing import Callable, TypedDict, Awaitable, Union
|
18 |
+
# ====================================================================
|
19 |
|
20 |
import httpx
|
21 |
|
|
|
51 |
"""
|
52 |
self.client = client
|
53 |
self.coins = coins
|
54 |
+
self._prices: dict[str, Union[float, str]] = {coin: "--" for coin in coins}
|
55 |
self._lock = asyncio.Lock() # Lock to prevent race conditions on the cache
|
56 |
self.sources: list[PriceSource] = self._configure_sources()
|
57 |
|
|
|
97 |
except (KeyError, TypeError, ValueError) as e:
|
98 |
logging.error("β [CoinCap] Failed to parse response: %s", e)
|
99 |
return {}
|
100 |
+
|
101 |
+
# ====================================================================
|
102 |
+
# FIX APPLIED HERE (2 of 2)
|
103 |
+
# ====================================================================
|
104 |
+
# Changed the type hint from `float | str` to `Union[float, str]`.
|
105 |
+
def get_current_prices(self) -> dict[str, Union[float, str]]:
|
106 |
+
# ====================================================================
|
107 |
"""Returns a copy of the current price cache. Thread-safe read."""
|
108 |
return self._prices.copy()
|
109 |
|
|
|
142 |
# Brief pause before trying the next API source
|
143 |
await asyncio.sleep(1)
|
144 |
|
145 |
+
logging.error("β All price APIs failed. Retaining stale prices.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|