fix(lint): auto-fix import sorting and unused imports across remaining files
ruff auto-fix pass: I001 import ordering and F401 unused imports across pg_shared.py, mercari/adapter.py, scheduler.py, and test files.
This commit is contained in:
parent
5db890c9f9
commit
ad687fabfd
18 changed files with 41 additions and 30 deletions
|
|
@ -127,6 +127,7 @@ CF_APP_NAME=snipe
|
||||||
# ── In-app feedback (beta) ────────────────────────────────────────────────────
|
# ── In-app feedback (beta) ────────────────────────────────────────────────────
|
||||||
# When set, a feedback FAB appears in the UI and routes submissions to Forgejo.
|
# When set, a feedback FAB appears in the UI and routes submissions to Forgejo.
|
||||||
# Leave unset to silently hide the button (demo/offline deployments).
|
# Leave unset to silently hide the button (demo/offline deployments).
|
||||||
# FORGEJO_API_TOKEN=
|
# FORGEJO_API_TOKEN= # dev/admin token
|
||||||
|
# FORGEJO_BOT_TOKEN= # cf-bugbot token — used for in-app feedback; falls back to FORGEJO_API_TOKEN
|
||||||
# FORGEJO_REPO=Circuit-Forge/snipe
|
# FORGEJO_REPO=Circuit-Forge/snipe
|
||||||
# FORGEJO_API_URL=https://git.opensourcesolarpunk.com/api/v1
|
# FORGEJO_API_URL=https://git.opensourcesolarpunk.com/api/v1
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import psycopg2
|
|
||||||
from psycopg2.pool import ThreadedConnectionPool
|
from psycopg2.pool import ThreadedConnectionPool
|
||||||
|
|
||||||
from app.db.models import MarketComp, ScammerEntry, Seller
|
from app.db.models import MarketComp, ScammerEntry, Seller
|
||||||
|
|
@ -262,9 +261,10 @@ class SnipeSharedStore:
|
||||||
listing_store must be provided (it's always the per-user SQLite Store).
|
listing_store must be provided (it's always the per-user SQLite Store).
|
||||||
Returns count of sellers updated.
|
Returns count of sellers updated.
|
||||||
"""
|
"""
|
||||||
from app.platforms.ebay.scraper import _classify_category_label # lazy to avoid circular
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from app.platforms.ebay.scraper import _classify_category_label # lazy to avoid circular
|
||||||
|
|
||||||
if not seller_ids or listing_store is None:
|
if not seller_ids or listing_store is None:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import logging
|
||||||
import time
|
import time
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from app.db.models import Listing, MarketComp, Seller
|
from app.db.models import Listing, Seller
|
||||||
from app.db.store import Store
|
from app.db.store import Store
|
||||||
from app.platforms import PlatformAdapter, SearchFilters
|
from app.platforms import PlatformAdapter, SearchFilters
|
||||||
from app.platforms.mercari.scraper import (
|
from app.platforms.mercari.scraper import (
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,9 @@ from pathlib import Path
|
||||||
|
|
||||||
from circuitforge_core.tasks.scheduler import (
|
from circuitforge_core.tasks.scheduler import (
|
||||||
TaskScheduler, # re-export for tests
|
TaskScheduler, # re-export for tests
|
||||||
)
|
)
|
||||||
from circuitforge_core.tasks.scheduler import (
|
from circuitforge_core.tasks.scheduler import (
|
||||||
get_scheduler as _base_get_scheduler,
|
get_scheduler as _base_get_scheduler,
|
||||||
reset_scheduler, # re-export for lifespan teardown
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from app.tasks.runner import LLM_TASK_TYPES, VRAM_BUDGETS, run_task
|
from app.tasks.runner import LLM_TASK_TYPES, VRAM_BUDGETS, run_task
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ theme:
|
||||||
name: Switch to dark mode
|
name: Switch to dark mode
|
||||||
- scheme: slate
|
- scheme: slate
|
||||||
primary: deep orange
|
primary: deep orange
|
||||||
accent: orange
|
accent: amber
|
||||||
toggle:
|
toggle:
|
||||||
icon: material/brightness-4
|
icon: material/brightness-4
|
||||||
name: Switch to light mode
|
name: Switch to light mode
|
||||||
|
|
@ -62,5 +62,8 @@ nav:
|
||||||
- Tier System: reference/tier-system.md
|
- Tier System: reference/tier-system.md
|
||||||
- Architecture: reference/architecture.md
|
- Architecture: reference/architecture.md
|
||||||
|
|
||||||
|
extra_css:
|
||||||
|
- stylesheets/theme.css
|
||||||
|
|
||||||
extra_javascript:
|
extra_javascript:
|
||||||
- plausible.js
|
- plausible.js
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,14 @@
|
||||||
Run inside the container:
|
Run inside the container:
|
||||||
docker exec -it snipe-api-1 python /app/snipe/scripts/debug_fetch_fresh.py
|
docker exec -it snipe-api-1 python /app/snipe/scripts/debug_fetch_fresh.py
|
||||||
"""
|
"""
|
||||||
import sys, time, threading
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
sys.path.insert(0, '/app/snipe')
|
sys.path.insert(0, '/app/snipe')
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
from app.platforms.ebay.browser_pool import BrowserPool, _close_slot
|
from app.platforms.ebay.browser_pool import BrowserPool, _close_slot
|
||||||
|
|
||||||
URL = "https://www.mercari.com/search/?keyword=rtx+4090&sortBy=SORT_SCORE&priceMax=800"
|
URL = "https://www.mercari.com/search/?keyword=rtx+4090&sortBy=SORT_SCORE&priceMax=800"
|
||||||
|
|
@ -34,6 +38,7 @@ print(f"Pool size after warmup: {pool2._q.qsize()}", flush=True)
|
||||||
|
|
||||||
# Grab a slot and close it (simulating the thread-error path)
|
# Grab a slot and close it (simulating the thread-error path)
|
||||||
import queue
|
import queue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
slot = pool2._q.get(timeout=3.0)
|
slot = pool2._q.get(timeout=3.0)
|
||||||
print(f"Got slot on display :{slot.display_num}", flush=True)
|
print(f"Got slot on display :{slot.display_num}", flush=True)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ from __future__ import annotations
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
_display_counter = itertools.count(200)
|
_display_counter = itertools.count(200)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""Tests for SnipeSharedStore — requires live Postgres via SNIPE_SHARED_DB_URL."""
|
"""Tests for SnipeSharedStore — requires live Postgres via SNIPE_SHARED_DB_URL."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.db.models import MarketComp, Seller
|
from app.db.models import MarketComp, Seller
|
||||||
from app.db.pg_shared import SnipeSharedDB, SnipeSharedStore
|
from app.db.pg_shared import SnipeSharedDB, SnipeSharedStore
|
||||||
from app.db.protocol import SharedTableProtocol
|
from app.db.protocol import SharedTableProtocol
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,12 @@ def test_store_clone_returns_new_instance(tmp_path):
|
||||||
|
|
||||||
|
|
||||||
def test_ebay_adapter_accepts_protocol():
|
def test_ebay_adapter_accepts_protocol():
|
||||||
from app.platforms.ebay.adapter import EbayAdapter
|
|
||||||
import tempfile
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import tempfile
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from app.platforms.ebay.adapter import EbayAdapter
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
s = Store(pathlib.Path(tmp) / "t.db")
|
s = Store(pathlib.Path(tmp) / "t.db")
|
||||||
adapter = EbayAdapter(token_manager=MagicMock(), shared_store=s)
|
adapter = EbayAdapter(token_manager=MagicMock(), shared_store=s)
|
||||||
|
|
@ -29,9 +30,10 @@ def test_ebay_adapter_accepts_protocol():
|
||||||
|
|
||||||
|
|
||||||
def test_scraped_adapter_no_db_path_ref():
|
def test_scraped_adapter_no_db_path_ref():
|
||||||
from app.platforms.ebay.scraper import ScrapedEbayAdapter
|
|
||||||
import tempfile
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from app.platforms.ebay.scraper import ScrapedEbayAdapter
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
s = Store(pathlib.Path(tmp) / "t.db")
|
s = Store(pathlib.Path(tmp) / "t.db")
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,11 @@ Verifies:
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
|
||||||
# ── Fixtures ──────────────────────────────────────────────────────────────────
|
# ── Fixtures ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -109,8 +107,8 @@ def test_async_search_empty_query(client):
|
||||||
assert data["status"] == "queued"
|
assert data["status"] == "queued"
|
||||||
assert "session_id" in data
|
assert "session_id" in data
|
||||||
|
|
||||||
|
|
||||||
from api.main import _update_queues
|
from api.main import _update_queues
|
||||||
import queue as _queue
|
|
||||||
sid = data["session_id"]
|
sid = data["session_id"]
|
||||||
assert sid in _update_queues
|
assert sid in _update_queues
|
||||||
q = _update_queues[sid]
|
q = _update_queues[sid]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
"""Unit tests for EbayCategoryCache."""
|
"""Unit tests for EbayCategoryCache."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
"""Tests for PATCH /api/preferences display.currency validation."""
|
"""Tests for PATCH /api/preferences display.currency validation."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|
@ -21,8 +20,9 @@ def client(tmp_path):
|
||||||
"""
|
"""
|
||||||
db_path = tmp_path / "snipe.db"
|
db_path = tmp_path / "snipe.db"
|
||||||
# Ensure the DB is initialised so the Store can create its tables.
|
# Ensure the DB is initialised so the Store can create its tables.
|
||||||
import api.cloud_session as _cs
|
|
||||||
from circuitforge_core.db import get_connection, run_migrations
|
from circuitforge_core.db import get_connection, run_migrations
|
||||||
|
|
||||||
|
import api.cloud_session as _cs
|
||||||
conn = get_connection(db_path)
|
conn = get_connection(db_path)
|
||||||
run_migrations(conn, Path("app/db/migrations"))
|
run_migrations(conn, Path("app/db/migrations"))
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,11 @@ from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.llm.query_translator import QueryTranslator, QueryTranslatorError, SearchParamsResponse, _parse_response
|
from app.llm.query_translator import (
|
||||||
|
QueryTranslator,
|
||||||
|
QueryTranslatorError,
|
||||||
|
_parse_response,
|
||||||
|
)
|
||||||
|
|
||||||
# ── _parse_response ───────────────────────────────────────────────────────────
|
# ── _parse_response ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -75,9 +78,10 @@ def test_parse_response_missing_required_field():
|
||||||
|
|
||||||
# ── Fixtures ──────────────────────────────────────────────────────────────────
|
# ── Fixtures ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
from app.platforms.ebay.categories import EbayCategoryCache
|
|
||||||
from circuitforge_core.db import get_connection, run_migrations
|
from circuitforge_core.db import get_connection, run_migrations
|
||||||
|
|
||||||
|
from app.platforms.ebay.categories import EbayCategoryCache
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def db_with_categories(tmp_path):
|
def db_with_categories(tmp_path):
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,11 @@ Covers:
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import queue as _queue
|
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
# ── Helpers ───────────────────────────────────────────────────────────────────
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def _clear_cache():
|
def _clear_cache():
|
||||||
|
|
@ -41,8 +38,9 @@ def isolated_cache():
|
||||||
def client(tmp_path):
|
def client(tmp_path):
|
||||||
"""TestClient backed by a fresh tmp DB."""
|
"""TestClient backed by a fresh tmp DB."""
|
||||||
os.environ["SNIPE_DB"] = str(tmp_path / "snipe.db")
|
os.environ["SNIPE_DB"] = str(tmp_path / "snipe.db")
|
||||||
from api.main import app
|
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from api.main import app
|
||||||
return TestClient(app, raise_server_exceptions=False)
|
return TestClient(app, raise_server_exceptions=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -238,6 +236,7 @@ def test_evict_is_rate_limited():
|
||||||
def test_async_cache_hit_skips_scraper(client, tmp_path):
|
def test_async_cache_hit_skips_scraper(client, tmp_path):
|
||||||
"""On a warm cache hit the scraper adapter must not be called."""
|
"""On a warm cache hit the scraper adapter must not be called."""
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import api.main as _main
|
import api.main as _main
|
||||||
from api.main import _cache_key
|
from api.main import _cache_key
|
||||||
|
|
||||||
|
|
@ -292,6 +291,7 @@ def test_async_cache_hit_skips_scraper(client, tmp_path):
|
||||||
def test_async_cache_miss_stores_result(client, tmp_path):
|
def test_async_cache_miss_stores_result(client, tmp_path):
|
||||||
"""After a cache miss the result must be stored in _search_result_cache."""
|
"""After a cache miss the result must be stored in _search_result_cache."""
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import api.main as _main
|
import api.main as _main
|
||||||
from api.main import _cache_key
|
from api.main import _cache_key
|
||||||
|
|
||||||
|
|
@ -353,6 +353,7 @@ def test_async_cache_miss_stores_result(client, tmp_path):
|
||||||
def test_async_refresh_bypasses_cache_read(client, tmp_path):
|
def test_async_refresh_bypasses_cache_read(client, tmp_path):
|
||||||
"""refresh=True must bypass cache read and invoke the scraper."""
|
"""refresh=True must bypass cache read and invoke the scraper."""
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
import api.main as _main
|
import api.main as _main
|
||||||
from api.main import _cache_key
|
from api.main import _cache_key
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,12 @@ from __future__ import annotations
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.tasks.monitor import _AUCTION_ALERT_WINDOW_HOURS, should_alert
|
from app.tasks.monitor import _AUCTION_ALERT_WINDOW_HOURS, should_alert
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# should_alert — pure function, no I/O
|
# should_alert — pure function, no I/O
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import MagicMock, patch, call
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue