From 56f05641182d5ba58945ff2f128bfa00ec24078f Mon Sep 17 00:00:00 2001 From: Erfouni Date: Wed, 30 Sep 2026 11:20:01 +0330 Subject: [PATCH] Install Pillow so capture_view actually downscales capture_view shrinks the plotted PNG with Pillow, as the README says, but Pillow wasn't in requirements.txt, so a fresh install sent every render at full plot size. Pin Pillow 12.3.0 (12.2.0 has open advisories) and add a test that builds a PNG without Pillow and checks it comes back 1100 px wide. Co-Authored-By: Claude Opus 5.5 --- README.md | 4 +-- requirements.txt | 4 +++ tests/test_shrink_png.py | 66 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 tests/test_shrink_png.py diff --git a/README.md b/README.md index a36f6a6..1872acc 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ Pick Codex when you want the safety controls. pip install -r requirements.txt ``` -Both dependencies were already installed on this machine; the file pins the -versions this was verified against. +The file pins the versions this was verified against. Pillow is what +`capture_view` uses to downscale its render; without it you get the full plot. ## Running it diff --git a/requirements.txt b/requirements.txt index 0455f35..1897b9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,9 @@ # The pins record what the server was verified against: pywin32 live with # AutoCAD on 2026-07-30; mcp raised from 1.27.1 to 1.28.1 on 2026-09-21 to # clear three published advisories, checked by tests/test_assembled_server.py. +# Pillow is what lets capture_view downscale its render +# (tests/test_shrink_png.py); 12.3.0 is clear of the advisories open +# against 12.2.0. mcp==1.28.1 pywin32==311 +Pillow==12.3.0 diff --git a/tests/test_shrink_png.py b/tests/test_shrink_png.py new file mode 100644 index 0000000..6927a01 --- /dev/null +++ b/tests/test_shrink_png.py @@ -0,0 +1,66 @@ +"""capture_view downscales its render with Pillow. + +tools._shrink_png is what keeps the plotted PNG cheap for a model to look at. +Without Pillow it quietly returns the file at full plot size, so the input PNG +here is written by hand (no Pillow needed) and the tests fail if nothing shrank. + + python -m unittest discover -s tests -t . +""" + +import os +import struct +import tempfile +import unittest +import zlib +from pathlib import Path + +# Must be in place before config is first imported, or that import writes a +# .secret file into the source tree. +os.environ.setdefault("ACAD_MCP_SECRET_PATH", "unit-test-secret") + +import tools # noqa: E402 + + +def write_png(path: Path, width: int, height: int) -> None: + """A plain 8-bit grayscale PNG, built with zlib alone.""" + + def chunk(kind: bytes, data: bytes) -> bytes: + crc = zlib.crc32(kind + data) & 0xFFFFFFFF + return struct.pack(">I", len(data)) + kind + data + struct.pack(">I", crc) + + row = b"\x00" + bytes(x % 256 for x in range(width)) + path.write_bytes( + b"\x89PNG\r\n\x1a\n" + + chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 0, 0, 0, 0)) + + chunk(b"IDAT", zlib.compress(row * height)) + + chunk(b"IEND", b"") + ) + + +def png_size(data: bytes) -> tuple[int, int]: + if data[:8] != b"\x89PNG\r\n\x1a\n": + raise AssertionError("not a PNG") + return struct.unpack(">II", data[16:24]) + + +class ShrinkPng(unittest.TestCase): + def setUp(self): + scratch = tempfile.TemporaryDirectory() + self.addCleanup(scratch.cleanup) + self.png = Path(scratch.name) / "view.png" + + def test_large_render_comes_back_at_the_limit(self): + write_png(self.png, 2400, 1600) + data, dimensions = tools._shrink_png(self.png, 1100) + self.assertEqual(png_size(data), (1100, 733)) + self.assertEqual(dimensions, "1100x733, rendered at 2400x1600") + + def test_small_render_keeps_its_size(self): + write_png(self.png, 800, 600) + data, dimensions = tools._shrink_png(self.png, 1100) + self.assertEqual(png_size(data), (800, 600)) + self.assertEqual(dimensions, "800x600") + + +if __name__ == "__main__": + unittest.main()