chore: enforce linting as part of build

This commit is contained in:
Bu5hm4nn
2026-03-24 00:26:36 +01:00
parent de03bd0064
commit 140a21c0b6
8 changed files with 58 additions and 47 deletions

View File

@@ -30,6 +30,11 @@
- Iterate toward partial progress while keeping the failure scoped and observable (Orange). - Iterate toward partial progress while keeping the failure scoped and observable (Orange).
- Finish only when the behavior is implemented and the test loop passes cleanly (Green). - Finish only when the behavior is implemented and the test loop passes cleanly (Green).
7. **Linting is part of the build, not an optional extra step.**
- `make build` must enforce linting first.
- Do not treat lint as a separate, skippable preflight.
- If the build is green, lint must already be green.
## Project Learnings ## Project Learnings
### NiceGUI layout constraint ### NiceGUI layout constraint

View File

@@ -1,4 +1,4 @@
.PHONY: install dev test build deploy .PHONY: install dev lint test build deploy
install: install:
python3 -m venv .venv python3 -m venv .venv
@@ -7,10 +7,14 @@ install:
dev: dev:
. .venv/bin/activate && python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 . .venv/bin/activate && python -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
lint:
. .venv/bin/activate && ruff check app tests scripts
. .venv/bin/activate && black --check app tests scripts
test: test:
. .venv/bin/activate && pytest . .venv/bin/activate && pytest
build: build: lint
docker build -t vault-dash . docker build -t vault-dash .
deploy: deploy:

View File

@@ -129,7 +129,7 @@ class PortfolioConfig:
def margin_call_price(self) -> float: def margin_call_price(self) -> float:
"""Calculate gold price at which margin call occurs.""" """Calculate gold price at which margin call occurs."""
if self.margin_threshold == 0: if self.margin_threshold == 0:
return float('inf') return float("inf")
return self.loan_amount / self.margin_threshold return self.loan_amount / self.margin_threshold
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, Any]:

View File

@@ -81,6 +81,7 @@ def get_cache() -> CacheService:
global _cache_instance global _cache_instance
if _cache_instance is None: if _cache_instance is None:
import os import os
redis_url = os.environ.get("REDIS_URL") redis_url = os.environ.get("REDIS_URL")
_cache_instance = CacheService(redis_url) _cache_instance = CacheService(redis_url)
return _cache_instance return _cache_instance

View File

@@ -110,7 +110,9 @@ class DataService:
await self.cache.set_json(cache_key, payload) await self.cache.set_json(cache_key, payload)
return payload return payload
async def get_options_chain_for_expiry(self, symbol: str | None = None, expiry: str | None = None) -> dict[str, Any]: async def get_options_chain_for_expiry(
self, symbol: str | None = None, expiry: str | None = None
) -> dict[str, Any]:
ticker_symbol = (symbol or self.default_symbol).upper() ticker_symbol = (symbol or self.default_symbol).upper()
expirations_data = await self.get_option_expirations(ticker_symbol) expirations_data = await self.get_option_expirations(ticker_symbol)
expirations = list(expirations_data.get("expirations") or []) expirations = list(expirations_data.get("expirations") or [])
@@ -176,7 +178,9 @@ class DataService:
await self.cache.set_json(cache_key, payload) await self.cache.set_json(cache_key, payload)
return payload return payload
except Exception as exc: # pragma: no cover - network dependent except Exception as exc: # pragma: no cover - network dependent
logger.warning("Failed to fetch options chain for %s %s from yfinance: %s", ticker_symbol, target_expiry, exc) logger.warning(
"Failed to fetch options chain for %s %s from yfinance: %s", ticker_symbol, target_expiry, exc
)
payload = self._fallback_options_chain( payload = self._fallback_options_chain(
ticker_symbol, ticker_symbol,
quote, quote,

View File

@@ -3,7 +3,7 @@
import asyncio import asyncio
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime
from typing import Optional from typing import Optional
import yfinance as yf import yfinance as yf
@@ -16,6 +16,7 @@ logger = logging.getLogger(__name__)
@dataclass @dataclass
class PriceData: class PriceData:
"""Price data for a symbol.""" """Price data for a symbol."""
symbol: str symbol: str
price: float price: float
currency: str currency: str
@@ -61,9 +62,9 @@ class PriceFeed:
"price": data.price, "price": data.price,
"currency": data.currency, "currency": data.currency,
"timestamp": data.timestamp.isoformat(), "timestamp": data.timestamp.isoformat(),
"source": data.source "source": data.source,
}, },
ttl=self.CACHE_TTL_SECONDS ttl=self.CACHE_TTL_SECONDS,
) )
return data return data
except Exception as e: except Exception as e:
@@ -85,12 +86,7 @@ class PriceFeed:
last_price = hist["Close"].iloc[-1] last_price = hist["Close"].iloc[-1]
currency = ticker.info.get("currency", "USD") currency = ticker.info.get("currency", "USD")
return PriceData( return PriceData(symbol=symbol, price=float(last_price), currency=currency, timestamp=datetime.utcnow())
symbol=symbol,
price=float(last_price),
currency=currency,
timestamp=datetime.utcnow()
)
return None return None
async def get_prices(self, symbols: list[str]) -> dict[str, Optional[PriceData]]: async def get_prices(self, symbols: list[str]) -> dict[str, Optional[PriceData]]:

View File

@@ -13,6 +13,7 @@ select = ["E4", "E7", "E9", "F", "I"]
[tool.black] [tool.black]
line-length = 120 line-length = 120
target-version = ["py312"]
extend-exclude = ''' extend-exclude = '''
/( /(
app/components app/components

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from playwright.sync_api import Page, expect, sync_playwright from playwright.sync_api import expect, sync_playwright
BASE_URL = "http://127.0.0.1:8000" BASE_URL = "http://127.0.0.1:8000"
ARTIFACTS = Path("tests/artifacts") ARTIFACTS = Path("tests/artifacts")