(self, async_client: AsyncOpenAI)
| 1534 | |
| 1535 | @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") |
| 1536 | def test_copy_build_request(self, async_client: AsyncOpenAI) -> None: |
| 1537 | options = FinalRequestOptions(method="get", url="/foo") |
| 1538 | |
| 1539 | def build_request(options: FinalRequestOptions) -> None: |
| 1540 | client_copy = async_client.copy() |
| 1541 | client_copy._build_request(options) |
| 1542 | |
| 1543 | # ensure that the machinery is warmed up before tracing starts. |
| 1544 | build_request(options) |
| 1545 | gc.collect() |
| 1546 | |
| 1547 | tracemalloc.start(1000) |
| 1548 | |
| 1549 | snapshot_before = tracemalloc.take_snapshot() |
| 1550 | |
| 1551 | ITERATIONS = 10 |
| 1552 | for _ in range(ITERATIONS): |
| 1553 | build_request(options) |
| 1554 | |
| 1555 | gc.collect() |
| 1556 | snapshot_after = tracemalloc.take_snapshot() |
| 1557 | |
| 1558 | tracemalloc.stop() |
| 1559 | |
| 1560 | def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: |
| 1561 | if diff.count == 0: |
| 1562 | # Avoid false positives by considering only leaks (i.e. allocations that persist). |
| 1563 | return |
| 1564 | |
| 1565 | if diff.count % ITERATIONS != 0: |
| 1566 | # Avoid false positives by considering only leaks that appear per iteration. |
| 1567 | return |
| 1568 | |
| 1569 | for frame in diff.traceback: |
| 1570 | if any( |
| 1571 | frame.filename.endswith(fragment) |
| 1572 | for fragment in [ |
| 1573 | # to_raw_response_wrapper leaks through the @functools.wraps() decorator. |
| 1574 | # |
| 1575 | # removing the decorator fixes the leak for reasons we don't understand. |
| 1576 | "openai/_legacy_response.py", |
| 1577 | "openai/_response.py", |
| 1578 | # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. |
| 1579 | "openai/_compat.py", |
| 1580 | # Standard library leaks we don't care about. |
| 1581 | "/logging/__init__.py", |
| 1582 | ] |
| 1583 | ): |
| 1584 | return |
| 1585 | |
| 1586 | leaks.append(diff) |
| 1587 | |
| 1588 | leaks: list[tracemalloc.StatisticDiff] = [] |
| 1589 | for diff in snapshot_after.compare_to(snapshot_before, "traceback"): |
| 1590 | add_leak(leaks, diff) |
| 1591 | if leaks: |
| 1592 | for leak in leaks: |
| 1593 | print("MEMORY LEAK:", leak) |
nothing calls this directly
no test coverage detected