Ruff linting.

This commit is contained in:
2026-05-02 03:31:51 +00:00
parent 8d89c397a4
commit 20d8d317fa
7 changed files with 33 additions and 27 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ def create_startup_box(
):
"""Create a framed startup box with server information."""
title = f"Cista {cista.__version__}"
listen = unix if unix else url
listen = unix or url
location = f"{folder} @ {listen}"
lines = [title, location]
# Auth line: Paskia <url> or Password, with optional Public suffix
+8 -8
View File
@@ -151,7 +151,7 @@ def setup_docker(confdir: Path | None = None) -> int:
logger.info("Building OnlyOffice image")
build_cmd = ["docker", "build", "-t", "onlyoffice-cista", str(docker_dir)]
logger.info("%s", " ".join(build_cmd))
result = subprocess.run(build_cmd)
result = subprocess.run(build_cmd, check=False, shell=False) # noqa: S603
if result.returncode != 0:
raise RuntimeError("Failed to build OnlyOffice image")
@@ -173,19 +173,19 @@ def setup_docker(confdir: Path | None = None) -> int:
"onlyoffice-cista",
]
logger.info("%s", " ".join(run_cmd))
result = subprocess.run(run_cmd)
result = subprocess.run(run_cmd, check=False, shell=False) # noqa: S603
if result.returncode != 0:
raise RuntimeError("Failed to start OnlyOffice container")
logger.info("OnlyOffice is running on http://localhost:8988")
return 0
async def is_available_async(timeout: float = 2.0) -> bool:
async def is_available_async(request_timeout: float = 2.0) -> bool:
"""Return True if the configured OnlyOffice Document Server is reachable."""
url = _get_onlyoffice_url().rstrip("/") + "/ConvertService.ashx"
client = get_httpx_client()
try:
response = await client.get(url, timeout=timeout)
response = await client.get(url, timeout=request_timeout)
return response.status_code in (200, 405)
except Exception:
return False
@@ -252,7 +252,7 @@ def _build_jwt_token(payload: dict) -> str | None:
return jwt.encode(payload, secret, algorithm="HS256")
async def convert_to_png_async(file_path: Path, timeout: float = 5.0) -> bytes:
async def convert_to_png_async(file_path: Path, request_timeout: float = 5.0) -> bytes:
"""Convert *file_path* to PNG using OnlyOffice Document Server (async).
Returns the PNG bytes. Raises RuntimeError on failure.
@@ -268,7 +268,7 @@ async def convert_to_png_async(file_path: Path, timeout: float = 5.0) -> bytes:
payload = {
"async": False,
"filetype": suffix,
"key": f"cista_{file_path.stat().st_mtime_ns}",
"key": f"cista_{(await asyncio.to_thread(file_path.stat)).st_mtime_ns}",
"outputtype": "png",
"title": file_path.name,
"url": doc_url,
@@ -286,7 +286,7 @@ async def convert_to_png_async(file_path: Path, timeout: float = 5.0) -> bytes:
convert_url,
content=json.dumps(payload).encode(),
headers=headers,
timeout=timeout,
timeout=request_timeout,
)
response.raise_for_status()
body = response.content
@@ -309,7 +309,7 @@ async def convert_to_png_async(file_path: Path, timeout: float = 5.0) -> bytes:
logger.debug("OnlyOffice converted in %.2fs: %s", t_end - t_start, file_url)
# Download converted PNG
png_response = await client.get(file_url, timeout=timeout)
png_response = await client.get(file_url, timeout=request_timeout)
png_response.raise_for_status()
return png_response.content
finally:
+16 -12
View File
@@ -32,7 +32,6 @@ from cista import auth, config, onlyoffice, sharefs
from cista.preview_worker import PreviewRequest, PreviewResponse
from cista.util.filename import sanitize
bp = Blueprint("preview", url_prefix="/preview")
@@ -179,14 +178,14 @@ class _PreviewWorkerPool:
_active_procs.add(proc)
try:
ready = await asyncio.wait_for(proc.stdout.readexactly(1), timeout=30.0)
except asyncio.TimeoutError:
except TimeoutError as err:
with contextlib.suppress(ProcessLookupError):
proc.kill()
raise WorkerProtocolError("preview worker failed to become ready")
except asyncio.IncompleteReadError:
raise WorkerProtocolError("preview worker failed to become ready") from err
except asyncio.IncompleteReadError as err:
raise WorkerProtocolError(
"preview worker exited before signalling readiness"
)
) from err
if ready != b"\x01":
raise WorkerProtocolError(f"preview worker ready signal invalid: {ready!r}")
return _PreviewWorker(proc)
@@ -420,11 +419,12 @@ class OOConversionManager:
def __init__(self, max_concurrent: int = OO_MAX_CONCURRENT):
self._semaphore = asyncio.Semaphore(max_concurrent)
self._in_flight: dict[str, asyncio.Future[bytes]] = {}
self._tasks: set[asyncio.Task[None]] = set()
self._lock = asyncio.Lock()
async def convert(self, filepath: Path) -> bytes:
"""Return PNG bytes for *filepath*, deduplicating concurrent requests."""
stat = filepath.stat()
stat = await asyncio.to_thread(filepath.stat)
key = f"{filepath}:{stat.st_mtime_ns}"
async with self._lock:
@@ -433,7 +433,9 @@ class OOConversionManager:
else:
future = asyncio.get_running_loop().create_future()
self._in_flight[key] = future
asyncio.create_task(self._do_convert(filepath, key, future))
task = asyncio.create_task(self._do_convert(filepath, key, future))
self._tasks.add(task)
task.add_done_callback(self._tasks.discard)
return await future
@@ -442,7 +444,9 @@ class OOConversionManager:
) -> None:
try:
async with self._semaphore:
png_bytes = await onlyoffice.convert_to_png_async(filepath, timeout=5.0)
png_bytes = await onlyoffice.convert_to_png_async(
filepath, request_timeout=5.0
)
except Exception as e:
future.set_exception(e)
async with self._lock:
@@ -630,13 +634,13 @@ async def preview(req, path):
_run_preview_process(filepath, quality, maxsize, maxzoom),
timeout=PREVIEW_TIMEOUT,
)
except asyncio.TimeoutError:
except TimeoutError:
logger.warning("Preview timeout for %s", filepath)
return empty(503)
except PreviewTimeoutError:
logger.warning("Preview worker timeout for %s", filepath)
return empty(503)
except httpx.HTTPStatusError as e:
except httpx.HTTPStatusError:
req.ctx._log_extra = "onlyoffice N/A"
return empty(503)
except httpx.RequestError:
@@ -775,8 +779,8 @@ def _image_via_ffmpeg(path: Path, maxsize: int, quality: int) -> bytes:
cmd.insert(4, "-s")
cmd.insert(5, f"{new_w}x{new_h}")
try:
subprocess.run(cmd, capture_output=True, check=True)
with open(tmp_path, "rb") as f:
subprocess.run(cmd, capture_output=True, check=True, shell=False) # noqa: S603
with Path(tmp_path).open("rb") as f:
return f.read()
finally:
Path(tmp_path).unlink(missing_ok=True)
+5 -3
View File
@@ -22,6 +22,8 @@ from pathlib import Path
import msgspec
from blake3 import blake3
logger = logging.getLogger(__name__)
class PreviewRequest(msgspec.Struct, omit_defaults=True):
path: str
@@ -121,7 +123,7 @@ def _run_loop() -> None:
)
_write_response(resp, result or b"")
except Exception as e:
logging.exception("Preview worker error for %s", req.path)
logger.exception("Preview worker error for %s", req.path)
captured = stderr_capture.getvalue().strip()
_write_response(
PreviewResponse(ok=False, error=str(e), stderr=captured or None), b""
@@ -138,13 +140,13 @@ def main() -> None:
from cista import config
config.load_config()
logging.warning(
logger.warning(
"preview-worker config=%s master_secret=%s",
config.conffile,
config.config.secret,
)
except Exception:
logging.exception("preview-worker failed to load config at startup")
logger.exception("preview-worker failed to load config at startup")
if len(sys.argv) > 1:
_run_once()
return
+1 -1
View File
@@ -257,7 +257,7 @@ async def proxy_auth_request(request):
method=request.method,
url=url,
headers=headers,
content=request.body if request.body else None,
content=request.body or None,
) as response:
raw_content = b"".join([chunk async for chunk in response.aiter_raw()])
+1 -1
View File
@@ -60,7 +60,7 @@ def websocket_wrapper(handler):
@wraps(handler)
async def wrapper(request, ws, *args, **kwargs):
username = getattr(request.ctx, "username", None)
extra = username if username else None
extra = username or None
start = time.perf_counter()
ws_id = log_ws_open(request, extra=extra)
close_extra = None