diff --git a/python/run.py b/python/run.py index c15d011..a346b1b 100644 --- a/python/run.py +++ b/python/run.py @@ -54,36 +54,28 @@ async def main() -> None: print(f"Fetching {len(urls)} URLs...\n") start = time.perf_counter() + count = 0 try: - results = await fetch_all( + async for r in fetch_all( urls, max_concurrent=max_concurrent, max_requests_per_second=max_requests_per_second, - ) - - elapsed = time.perf_counter() - start - - if not isinstance(results, list): - print(f"fetch_all returned {type(results).__name__}, expected list") - sys.exit(1) - - print(f"Got {len(results)} results in {elapsed:.2f}s\n") - - for i, r in enumerate(results): - if r is None: - print(f" [{i}] None") - elif isinstance(r, Failure): - print(f" [{i}] FAIL {r.url} — {r.error}") + ): + count += 1 + if isinstance(r, Failure): + print(f" [{count}] FAIL {r.url} — {r.error}") elif isinstance(r, Success): - print(f" [{i}] OK {r.url}") + print(f" [{count}] OK {r.url}") else: - print(f" [{i}] ??? {r!r}") - + print(f" [{count}] ??? {r!r}") except Exception as exc: elapsed = time.perf_counter() - start print(f"\nfetch_all raised after {elapsed:.2f}s: {exc}") sys.exit(1) + elapsed = time.perf_counter() - start + print(f"\nGot {count} results in {elapsed:.2f}s") + stats = _get("/stats") failed = False if stats["rateLimitViolations"] > 0: @@ -95,6 +87,8 @@ async def main() -> None: if failed: sys.exit(1) + else: + print(stats) if __name__ == "__main__": diff --git a/python/solution.py b/python/solution.py index beccc38..8ac665e 100644 --- a/python/solution.py +++ b/python/solution.py @@ -37,7 +37,8 @@ async def fetch_all( *, max_concurrent: int, max_requests_per_second: int, -) -> list[Result]: - """Fetch all URLs concurrently and return a Result for each, preserving order.""" +): + """Fetch all URLs concurrently, yielding a Result for each.""" # TODO: implement raise NotImplementedError + yield # makes this an async generator diff --git a/python/test_harness.py b/python/test_harness.py index 6aa87b8..7e62c70 100644 --- a/python/test_harness.py +++ b/python/test_harness.py @@ -58,25 +58,23 @@ async def main() -> None: # Run candidate solution # ------------------------------------------------------------------ start = time.perf_counter() + results_by_url: dict[str, object] = {} try: - results = await fetch_all( + async for r in fetch_all( urls, max_concurrent=10, max_requests_per_second=15, - ) + ): + results_by_url[r.url] = r except Exception as exc: print(f"FAIL — fetch_all raised an exception: {exc}") sys.exit(1) elapsed = time.perf_counter() - start - # ------------------------------------------------------------------ - # Validate return value - # ------------------------------------------------------------------ - if not isinstance(results, list): - print( - f"FAIL — fetch_all must return a list, got {type(results).__name__}: {results!r}" - ) - sys.exit(1) + results = [ + results_by_url.get(url, Failure(url=url, error="URL was not fetched")) + for url in urls + ] # ------------------------------------------------------------------ # Validate result shape diff --git a/typescript/server.ts b/typescript/server.ts index d0eb7f6..a7d47a5 100644 --- a/typescript/server.ts +++ b/typescript/server.ts @@ -80,7 +80,7 @@ type ItemHandler = (id: number, attempts: number, res: express.Response) => void /** Mode 1 — all items succeed immediately. */ function strategyAllSuccess(id: number, _attempts: number, res: express.Response): void { - res.json(items[id]); + setTimeout(() => res.json(items[id]), 20); } /** Mode 2 — mixed errors and delays based on ID range. */