|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Re-apply the SDK's transport request defaults after OpenAPI regeneration. |
| 3 | +
|
| 4 | +Two defaults the generator leaves on urllib3's (server-observable) behavior: |
| 5 | +
|
| 6 | +* ``Accept-Encoding``: urllib3 puts ``identity`` on every connection, which |
| 7 | + asks the server *not* to compress. We advertise |
| 8 | + ``urllib3.util.request.ACCEPT_ENCODING`` instead — exactly the codecs the |
| 9 | + installed urllib3 can transparently decode. |
| 10 | +* ``User-Agent``: ``OpenAPI-Generator/1.0.0/python`` identifies neither the SDK |
| 11 | + nor its version, so server-side telemetry cannot attribute traffic. |
| 12 | +""" |
| 13 | + |
| 14 | +from __future__ import annotations |
| 15 | + |
| 16 | +import pathlib |
| 17 | +import sys |
| 18 | + |
| 19 | +ROOT = pathlib.Path(__file__).resolve().parents[1] |
| 20 | + |
| 21 | + |
| 22 | +def patch_accept_encoding() -> None: |
| 23 | + """Advertise compression on every request made through RESTClientObject.""" |
| 24 | + path = ROOT / "hotdata" / "rest.py" |
| 25 | + src = path.read_text() |
| 26 | + |
| 27 | + if "ACCEPT_ENCODING" in src: |
| 28 | + return |
| 29 | + |
| 30 | + import_needle = "import urllib3\n" |
| 31 | + import_replacement = "import urllib3\nfrom urllib3.util.request import ACCEPT_ENCODING\n" |
| 32 | + if import_needle not in src: |
| 33 | + sys.exit(f"Failed to patch {path}: urllib3 import anchor not found") |
| 34 | + src = src.replace(import_needle, import_replacement, 1) |
| 35 | + |
| 36 | + needle = " post_params = post_params or {}\n headers = headers or {}\n" |
| 37 | + replacement = ( |
| 38 | + " post_params = post_params or {}\n" |
| 39 | + " headers = headers or {}\n\n" |
| 40 | + " # Ask for compressed responses. urllib3 defaults every connection to\n" |
| 41 | + " # `Accept-Encoding: identity`, which is not \"no preference\" but an\n" |
| 42 | + " # explicit request *not* to compress, and a spec-compliant server\n" |
| 43 | + " # honors it. ACCEPT_ENCODING is built from the codecs the installed\n" |
| 44 | + " # urllib3 can actually decode (gzip/deflate, plus br/zstd when their\n" |
| 45 | + " # backends are present), so the server can never negotiate an encoding\n" |
| 46 | + " # that reaches us as undecodable bytes. urllib3 decodes the body\n" |
| 47 | + " # transparently, so callers are unaffected.\n" |
| 48 | + " #\n" |
| 49 | + " # A default, not an override: an operation whose payload is already\n" |
| 50 | + " # compressed end-to-end passes `identity` and stays in control. The\n" |
| 51 | + " # check is case-insensitive because header names are -- a caller\n" |
| 52 | + " # passing `accept-encoding` would otherwise leave both keys in the\n" |
| 53 | + " # dict and urllib3 would emit two header lines, so the server would\n" |
| 54 | + " # see the opt-out *and* the compressed set. urllib3 lowercases names\n" |
| 55 | + " # the same way before adding its own Accept-Encoding.\n" |
| 56 | + " if not any(key.lower() == 'accept-encoding' for key in headers):\n" |
| 57 | + " headers['Accept-Encoding'] = ACCEPT_ENCODING\n" |
| 58 | + ) |
| 59 | + if needle not in src: |
| 60 | + sys.exit(f"Failed to patch {path}: request() header anchor not found") |
| 61 | + src = src.replace(needle, replacement, 1) |
| 62 | + |
| 63 | + path.write_text(src) |
| 64 | + |
| 65 | + |
| 66 | +def patch_user_agent() -> None: |
| 67 | + """Point ApiClient at the hand-maintained User-Agent constant. |
| 68 | +
|
| 69 | + The string itself lives in ``hotdata/_useragent.py`` (generator-ignored), so |
| 70 | + this patch is just an import plus the assignment — two anchors instead of |
| 71 | + carrying the logic inside generated output. |
| 72 | + """ |
| 73 | + path = ROOT / "hotdata" / "api_client.py" |
| 74 | + src = path.read_text() |
| 75 | + |
| 76 | + if "_useragent" in src: |
| 77 | + return |
| 78 | + |
| 79 | + import_needle = "from hotdata.api_response import ApiResponse, T as ApiResponseT\n" |
| 80 | + import_replacement = ( |
| 81 | + "from hotdata.api_response import ApiResponse, T as ApiResponseT\n" |
| 82 | + "from hotdata._useragent import USER_AGENT\n" |
| 83 | + ) |
| 84 | + if import_needle not in src: |
| 85 | + sys.exit(f"Failed to patch {path}: api_response import anchor not found") |
| 86 | + src = src.replace(import_needle, import_replacement, 1) |
| 87 | + |
| 88 | + ua_needle = " self.user_agent = 'OpenAPI-Generator/1.0.0/python'\n" |
| 89 | + ua_replacement = " self.user_agent = USER_AGENT\n" |
| 90 | + if ua_needle not in src: |
| 91 | + sys.exit(f"Failed to patch {path}: default User-Agent anchor not found") |
| 92 | + src = src.replace(ua_needle, ua_replacement, 1) |
| 93 | + |
| 94 | + path.write_text(src) |
| 95 | + |
| 96 | + |
| 97 | +def main() -> None: |
| 98 | + patch_accept_encoding() |
| 99 | + patch_user_agent() |
| 100 | + |
| 101 | + |
| 102 | +if __name__ == "__main__": |
| 103 | + main() |
0 commit comments