placekit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. placekit-0.1.0/.cargo/audit.toml +25 -0
  2. placekit-0.1.0/.gitignore +18 -0
  3. placekit-0.1.0/CHANGELOG.md +194 -0
  4. placekit-0.1.0/Cargo.lock +1556 -0
  5. placekit-0.1.0/Cargo.toml +35 -0
  6. placekit-0.1.0/LICENSE +21 -0
  7. placekit-0.1.0/MIGRATING.md +755 -0
  8. placekit-0.1.0/Makefile +24 -0
  9. placekit-0.1.0/NORMALIZATION.md +1020 -0
  10. placekit-0.1.0/PKG-INFO +325 -0
  11. placekit-0.1.0/README.md +290 -0
  12. placekit-0.1.0/crates/placekit-core/Cargo.toml +21 -0
  13. placekit-0.1.0/crates/placekit-core/src/address.rs +1369 -0
  14. placekit-0.1.0/crates/placekit-core/src/address_test_data.rs +360 -0
  15. placekit-0.1.0/crates/placekit-core/src/client.rs +4045 -0
  16. placekit-0.1.0/crates/placekit-core/src/errors.rs +3840 -0
  17. placekit-0.1.0/crates/placekit-core/src/lib.rs +37 -0
  18. placekit-0.1.0/crates/placekit-core/src/mask.rs +578 -0
  19. placekit-0.1.0/crates/placekit-core/src/models.rs +2016 -0
  20. placekit-0.1.0/pyproject.toml +52 -0
  21. placekit-0.1.0/python/placekit/__init__.py +145 -0
  22. placekit-0.1.0/python/placekit/__init__.pyi +126 -0
  23. placekit-0.1.0/python/placekit/_placekit.pyi +191 -0
  24. placekit-0.1.0/python/placekit/address.py +1431 -0
  25. placekit-0.1.0/python/placekit/address.pyi +342 -0
  26. placekit-0.1.0/python/placekit/client.py +1992 -0
  27. placekit-0.1.0/python/placekit/client.pyi +206 -0
  28. placekit-0.1.0/python/placekit/exceptions.py +392 -0
  29. placekit-0.1.0/python/placekit/exceptions.pyi +66 -0
  30. placekit-0.1.0/python/placekit/fields.py +589 -0
  31. placekit-0.1.0/python/placekit/fields.pyi +37 -0
  32. placekit-0.1.0/python/placekit/lang.py +273 -0
  33. placekit-0.1.0/python/placekit/lang.pyi +169 -0
  34. placekit-0.1.0/python/placekit/models.py +3044 -0
  35. placekit-0.1.0/python/placekit/models.pyi +907 -0
  36. placekit-0.1.0/python/placekit/py.typed +0 -0
  37. placekit-0.1.0/python/placekit/ranking.py +30 -0
  38. placekit-0.1.0/python/placekit/ranking.pyi +13 -0
  39. placekit-0.1.0/python/placekit/types.py +1641 -0
  40. placekit-0.1.0/python/placekit/types.pyi +1051 -0
  41. placekit-0.1.0/rust-toolchain.toml +13 -0
  42. placekit-0.1.0/scripts/build_corpus_fixtures.py +498 -0
  43. placekit-0.1.0/scripts/check_no_emdash.py +51 -0
  44. placekit-0.1.0/scripts/check_version.py +233 -0
  45. placekit-0.1.0/scripts/check_wheel_sbom.py +361 -0
  46. placekit-0.1.0/scripts/embed_full_sbom.py +203 -0
  47. placekit-0.1.0/src/lib.rs +1752 -0
  48. placekit-0.1.0/tests/conftest.py +342 -0
  49. placekit-0.1.0/tests/fixtures/autocomplete.json +1 -0
  50. placekit-0.1.0/tests/fixtures/coverage_manifest.json +2219 -0
  51. placekit-0.1.0/tests/fixtures/coverage_table.md +86 -0
  52. placekit-0.1.0/tests/fixtures/error_denied.json +1 -0
  53. placekit-0.1.0/tests/fixtures/error_invalid.json +1 -0
  54. placekit-0.1.0/tests/fixtures/error_not_found.json +1 -0
  55. placekit-0.1.0/tests/fixtures/error_quota.json +1 -0
  56. placekit-0.1.0/tests/fixtures/error_unauthenticated.json +1 -0
  57. placekit-0.1.0/tests/fixtures/nearby_search.json +1 -0
  58. placekit-0.1.0/tests/fixtures/photo.png +0 -0
  59. placekit-0.1.0/tests/fixtures/place_details.json +1 -0
  60. placekit-0.1.0/tests/fixtures/recorded/recorded_A10_jerusalem_no_region_code.json +1 -0
  61. placekit-0.1.0/tests/fixtures/recorded/recorded_A10_jerusalem_region_ps.json +1 -0
  62. placekit-0.1.0/tests/fixtures/recorded/recorded_A12_long_village_name.json +1 -0
  63. placekit-0.1.0/tests/fixtures/recorded/recorded_A13_premise_and_subpremise.json +1 -0
  64. placekit-0.1.0/tests/fixtures/recorded/recorded_A1_no_street_number.json +1 -0
  65. placekit-0.1.0/tests/fixtures/recorded/recorded_A2_no_locality_sublocality_city.json +1 -0
  66. placekit-0.1.0/tests/fixtures/recorded/recorded_A3_postal_town_carries_city.json +1 -0
  67. placekit-0.1.0/tests/fixtures/recorded/recorded_A4_locality_postal_town_disagree.json +1 -0
  68. placekit-0.1.0/tests/fixtures/recorded/recorded_A5_sublocality_chains.json +1 -0
  69. placekit-0.1.0/tests/fixtures/recorded/recorded_A6_japanese_block_addressing.json +1 -0
  70. placekit-0.1.0/tests/fixtures/recorded/recorded_A7_rtl_script_address.json +1 -0
  71. placekit-0.1.0/tests/fixtures/recorded/recorded_A8_tokyo_english.json +1 -0
  72. placekit-0.1.0/tests/fixtures/recorded/recorded_A8_tokyo_japanese.json +1 -0
  73. placekit-0.1.0/tests/fixtures/recorded/recorded_A9_country_without_postal_codes.json +1 -0
  74. placekit-0.1.0/tests/fixtures/recorded/recorded_H10_seasonal_offset_sample.json +1 -0
  75. placekit-0.1.0/tests/fixtures/recorded/recorded_H11_description_order_vs_period_anchor.json +1 -0
  76. placekit-0.1.0/tests/fixtures/recorded/recorded_H1_open_24_hours.json +1 -0
  77. placekit-0.1.0/tests/fixtures/recorded/recorded_H2_split_hours.json +1 -0
  78. placekit-0.1.0/tests/fixtures/recorded/recorded_H3_overnight_hours.json +1 -0
  79. placekit-0.1.0/tests/fixtures/recorded/recorded_H6_truncated_period_points.json +1 -0
  80. placekit-0.1.0/tests/fixtures/recorded/recorded_H7_secondary_hours.json +1 -0
  81. placekit-0.1.0/tests/fixtures/recorded/recorded_H8_fractional_utc_offset.json +1 -0
  82. placekit-0.1.0/tests/fixtures/recorded/recorded_H9_extreme_utc_offset.json +1 -0
  83. placekit-0.1.0/tests/fixtures/recorded/recorded_OBS1_requested_field_vanishes.json +1 -0
  84. placekit-0.1.0/tests/fixtures/recorded/recorded_OBS2_pure_service_area_depth_split.json +1 -0
  85. placekit-0.1.0/tests/fixtures/recorded/recorded_P10_ev_charging_full_detail.json +1 -0
  86. placekit-0.1.0/tests/fixtures/recorded/recorded_P11_fuel_options.json +1 -0
  87. placekit-0.1.0/tests/fixtures/recorded/recorded_P12_contained_place.json +1 -0
  88. placekit-0.1.0/tests/fixtures/recorded/recorded_P15_details_depth_saigon.json +1 -0
  89. placekit-0.1.0/tests/fixtures/recorded/recorded_P15_richest_mall_store.json +1 -0
  90. placekit-0.1.0/tests/fixtures/recorded/recorded_P1_no_address_natural_feature.json +1 -0
  91. placekit-0.1.0/tests/fixtures/recorded/recorded_P2_transit_station.json +1 -0
  92. placekit-0.1.0/tests/fixtures/recorded/recorded_P4_temporarily_closed.json +1 -0
  93. placekit-0.1.0/tests/fixtures/recorded/recorded_P7_no_photos_key.json +1 -0
  94. placekit-0.1.0/tests/fixtures/recorded/recorded_P8_details_depth_no_rating.json +1 -0
  95. placekit-0.1.0/tests/fixtures/recorded/recorded_P8_search_depth_no_rating.json +1 -0
  96. placekit-0.1.0/tests/fixtures/recorded/recorded_V10_long_form_place_id.json +1 -0
  97. placekit-0.1.0/tests/fixtures/recorded/recorded_V11_next_open_time_populated.json +1 -0
  98. placekit-0.1.0/tests/fixtures/recorded/recorded_V1_rating_absent_against_whole.json +1 -0
  99. placekit-0.1.0/tests/fixtures/recorded/recorded_V3_top_price_levels.json +1 -0
  100. placekit-0.1.0/tests/fixtures/recorded/recorded_V5_plus_code_global_only.json +1 -0
  101. placekit-0.1.0/tests/fixtures/synthesized/synthesized_A11_component_present_but_empty.json +1 -0
  102. placekit-0.1.0/tests/fixtures/synthesized/synthesized_P13_consumer_alert.json +1 -0
  103. placekit-0.1.0/tests/fixtures/synthesized/synthesized_P6_moved_place.json +1 -0
  104. placekit-0.1.0/tests/fixtures/synthesized/synthesized_V6_single_point_viewport.json +1 -0
  105. placekit-0.1.0/tests/fixtures/text_search.json +1 -0
  106. placekit-0.1.0/tests/test_address.py +596 -0
  107. placekit-0.1.0/tests/test_adversarial.py +751 -0
  108. placekit-0.1.0/tests/test_check_version.py +271 -0
  109. placekit-0.1.0/tests/test_check_wheel_sbom.py +530 -0
  110. placekit-0.1.0/tests/test_client.py +3832 -0
  111. placekit-0.1.0/tests/test_concurrency.py +216 -0
  112. placekit-0.1.0/tests/test_constants.py +202 -0
  113. placekit-0.1.0/tests/test_corpus_accounting.py +5613 -0
  114. placekit-0.1.0/tests/test_decode_copies.py +577 -0
  115. placekit-0.1.0/tests/test_embed_full_sbom.py +340 -0
  116. placekit-0.1.0/tests/test_migration_guide.py +268 -0
  117. placekit-0.1.0/tests/test_normalization_guide.py +944 -0
  118. placekit-0.1.0/tests/test_recorded_corpus.py +847 -0
  119. placekit-0.1.0/tests/test_round23.py +175 -0
  120. placekit-0.1.0/tests/test_round24.py +692 -0
  121. placekit-0.1.0/tests/test_round25.py +830 -0
  122. placekit-0.1.0/tests/test_round28.py +103 -0
  123. placekit-0.1.0/tests/test_round30.py +177 -0
  124. placekit-0.1.0/tests/test_round30_pump.py +125 -0
  125. placekit-0.1.0/tests/test_round31.py +373 -0
  126. placekit-0.1.0/tests/test_round34.py +147 -0
  127. placekit-0.1.0/tests/test_round35.py +126 -0
  128. placekit-0.1.0/tests/test_round37.py +123 -0
  129. placekit-0.1.0/tests/test_round41.py +351 -0
  130. placekit-0.1.0/tests/test_round45.py +135 -0
  131. placekit-0.1.0/tests/test_round47.py +131 -0
  132. placekit-0.1.0/tests/test_round52.py +13294 -0
  133. placekit-0.1.0/tests/test_round53.py +838 -0
  134. placekit-0.1.0/tests/test_round54.py +229 -0
  135. placekit-0.1.0/tests/test_round55.py +310 -0
  136. placekit-0.1.0/tests/test_round55_repair.py +1371 -0
  137. placekit-0.1.0/tests/test_round57.py +434 -0
  138. placekit-0.1.0/tests/test_round62.py +988 -0
  139. placekit-0.1.0/tests/test_round64.py +67 -0
  140. placekit-0.1.0/tests/test_round65.py +589 -0
  141. placekit-0.1.0/tests/test_round66.py +419 -0
  142. placekit-0.1.0/tests/test_round75.py +689 -0
  143. placekit-0.1.0/tests/test_round77.py +272 -0
  144. placekit-0.1.0/tests/test_round81.py +259 -0
  145. placekit-0.1.0/tests/test_synthesized_corpus.py +182 -0
@@ -0,0 +1,25 @@
1
+ # Advisory dispositions for cargo audit. Each ignored advisory
2
+ # below names a function in the locked PyO3 0.23.5 that no code
3
+ # in this workspace calls, so the vulnerable path is present in
4
+ # the compiled dependency and unreachable from anything this
5
+ # project ships. The Round 58 audit verified the reach by
6
+ # direct search of the workspace sources and of pythonize
7
+ # 0.23.0, the only other PyO3 consumer in the lock. An entry
8
+ # stands only while that reach statement stays true, and any
9
+ # advisory not named here still fails the audit.
10
+
11
+ [advisories]
12
+ ignore = [
13
+ # RUSTSEC-2025-0020. Risk of buffer overflow in PyString
14
+ # from_object and its bound variant. No caller of either
15
+ # function exists in the workspace sources, and PyString
16
+ # itself is unused there. pythonize 0.23.0 calls from_object
17
+ # only on its own Depythonizer type and on PyIterator,
18
+ # never on PyString.
19
+ "RUSTSEC-2025-0020",
20
+ # RUSTSEC-2026-0177. Missing Sync bound on closures handed
21
+ # to PyCFunction new_closure. No caller of new_closure
22
+ # exists in the workspace sources or in pythonize 0.23.0,
23
+ # and PyCFunction is unused in both.
24
+ "RUSTSEC-2026-0177",
25
+ ]
@@ -0,0 +1,18 @@
1
+ /target
2
+ **/target
3
+ dist/
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ __pycache__/
8
+ *.py[cod]
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .venv/
13
+ .venv-*/
14
+ venv/
15
+ *.so
16
+ *.pyd
17
+ python/placekit/_placekit*.so
18
+ .DS_Store
@@ -0,0 +1,194 @@
1
+ # Changelog
2
+
3
+ All notable changes to placekit are recorded here. The format follows Keep a Changelog, and versions follow Semantic Versioning.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.0] - 2026-11-12
8
+
9
+ ### Added
10
+
11
+ - README teachings and a wider spelling guard. The Quickstart photo passage now names `max_height_px` as the other member of the photo size pair beside `max_width_px`, each from 1 to 4800, and the Field masks passage now spells `FieldMask.of` and `FieldMask.header_value` where it teaches mask construction, with `normalize_fields` named beside them. The opening hours passage names `regular_opening_hours` beside `current_opening_hours`, the endpoints section names `search_uri` and `routing_summaries` on the search results, and the autocomplete passage names `suggestions` as the list the two prediction members read off. The opening paragraph now qualifies its raw claim for `PhotoMedia`, which carries no raw, the paging passage is scoped to Text Search and carries a stop caution for a repeated page token, and the spelling guard in tests/test_round57.py now covers all five client calls and the public members of `FieldMask`.
12
+ - Three more README teachings the consumer gauntlet showed missing. The Field masks section now states that search and nearby mask paths carry the `places` prefix while place details mask paths are bare field names, and that a details mask must still name `id` or `name` or the details call raises `DecodeError` even on a well-formed response. The endpoints table row and the Quickstart photo passage now name the `skip_http_redirect` keyword and the `photo_uri` attribute on `PhotoUriResult`, so the photo URI mode can be asked for from the guides alone. The session token paragraph now states that tokens are caller generated and a UUID is the usual shape.
13
+ - Three README teachings the consumer gauntlet showed missing. The Addresses section now names the `country_code` reader beside `postal_code` and states its plain string or None return against the `PostalCodeResult` object the postal reader returns, and its worked example starts from a `place_details` call with the warning that the address readers over a bare search result carrying neither address components nor a postal address answer quietly with None readings rather than an error. A new autocomplete passage names the `place_predictions` and `query_predictions` containers and teaches that a prediction's `text` is an optional `FormattedText`, with the display string at `prediction.text.text`, the match ranges at `prediction.text.matches`, and a None guard before the read. A new opening hours example fetches details and checks `current_opening_hours` before printing `current_opening_hours.open_now`, which can itself be None and prints as None, and the `weekday_descriptions` lines, naming the wire spelling `currentOpeningHours` as the same field under its other spelling and teaching that both levels of the path can be None.
14
+ - Request parameters that previously could not be expressed, across the board. Text Search gains `page_size`, `min_rating`, `ev_options`, `search_along_route`, `routing_parameters` and the two include booleans. Nearby Search gains `included_primary_types`, `excluded_primary_types`, `routing_parameters` and `include_future_opening_businesses`. Autocomplete gains `origin`, `input_offset`, `included_primary_types`, the three include booleans and an optional `fields` mask.
15
+ - `PhotoUriResult`, returned by `place_photo` with `skip_http_redirect=True` instead of following the media redirect, carrying the photo name and its short lived URI.
16
+ - `QueryPrediction`, modelled alongside `PlacePrediction` and exposed as `AutocompleteResult.query_predictions`, so query predictions are no longer dropped by the autocomplete surface.
17
+ - A lifecycle API on both clients, with `close()`, `with` on `Client` and `async with` on `AsyncClient`. Calls after close raise `ConfigError`.
18
+ - Typed response coverage for opening hour periods, address components, plus code, viewport, postal address, primary type display name and current opening hours on `Place`, the flag and maps URIs on `Photo`, the resource name on `PlacePrediction`, and `routing_summaries` on both search results, with `search_uri` carried by the text search result alone.
19
+ - Typed opening hours completion on `OpeningHours`, with `next_close_time` and `next_open_time` as unparsed timestamp strings, `special_days` carrying a new `SpecialDay` model, and `secondary_hours_type` naming the kind of a secondary hours entry. `Place` gains `current_secondary_opening_hours` and `regular_secondary_opening_hours` as lists of `OpeningHours`, and both join the Place Details default mask.
20
+ - `PostalAddress` completion with `sublocality`, `organization` and `recipients`, so the structured address carries the delivery names the API returns beside the locality fields.
21
+ - Nine more typed `Place` fields. `editorial_summary` and `google_maps_type_label` reuse `LocalizedText`, `opening_date` reuses `Date`, and `icon_mask_base_uri`, `icon_background_color`, `utc_offset_minutes`, `pure_service_area_business`, `moved_place` and `moved_place_id` land as scalars whose absence reads None rather than a fabricated value. All nine join the Place Details default mask.
22
+ - `Place.reviews` with a new `Review` model reusing `LocalizedText`, `AuthorAttribution` and `Date`, carrying the review text and its original language form, the rating as a float, the author, the publish time and the visit date. Reviews join the Place Details default mask and stay out of the search defaults, whose field tier for them is unchecked.
23
+ - The 23 dine, service and amenity flags on `Place`, from `takeout` and `delivery` through `serves_breakfast` to `good_for_watching_sports`, as optional booleans whose absence reads None and never a fabricated false. All 23 join the Place Details default mask.
24
+ - Three boolean option objects on `Place` with new `PaymentOptions`, `ParkingOptions` and `AccessibilityOptions` models, every leaf an optional boolean whose absence reads None. The three parents join the Place Details default mask.
25
+ - Strict decoding for those optional boolean leaves. A non boolean wire value in one of them raises `DecodeError` instead of being stored verbatim, so a switched off flag can no longer read as on.
26
+ - Four wrapper models on `Place`. `TimeZone` types only its id, `GenerativeSummary` and `ReviewSummary` type only their `LocalizedText` payload, and `ConsumerAlert` types its plain string overview and language, with the flag, disclosure, link and detail leaves beside them staying in raw. All four parents join the Place Details default mask.
27
+ - `GoogleMapsLinks` and `PriceRange` on `Place`, with a new `Money` model shared with the price range endpoints. Money units decode from the JSON string form the wire uses for int64 values to a real int, and an open ended price range keeps its absent end as None. Both parents join the Place Details default mask.
28
+ - The EV charging summary on `Place` as `EvChargeOptions` with `ConnectorAggregation`, typing the connector count, the per type charge rate and counts, and the availability counts with their last update time. The parent joins the Place Details default mask.
29
+ - `AddressDescriptor` on `Place` with new `Landmark` and `Area` models, typing the paired resource names and place ids, the display names, the landmark types and distances, and the area containment. The parent joins the Place Details default mask.
30
+ - Parsed error envelope details on every API exception, as `error_code`, `details`, `field_violations` and `quota_violations`.
31
+ - A configurable response size cap on the Rust core builder, defaulting to 32 MiB. The Python clients expose no setting for it, so the configurability stops at the Rust layer and a Python caller always runs with the default.
32
+ - Client side validation for `min_rating`, Nearby Search result counts, session token format, and mutually exclusive location bias and restriction values. Location values are now typed shapes end to end, so a circle in a Text Search restriction fails locally instead of at the API.
33
+ - `make check`, one gate for the whole repo (rustfmt, clippy, Rust tests, version coherence, em dash scan, Python tests), with a CI workflow that runs it word for word.
34
+ - `scripts/check_version.py`, which fails unless the version agrees across the Cargo manifests, the changelog, and the Python package, and doubles as the release tag guard in the publish workflow.
35
+ - `scripts/check_no_emdash.py`, a codepoint scan that keeps em dashes out of tracked text files.
36
+ - Core side validation of request coordinates, circle radii and EV charging rates, so non-finite or out-of-range values fail locally with a configuration error instead of serializing as JSON null or passing to the API unchanged.
37
+ - `Retry-After` headers in HTTP date form are read alongside the delta seconds form, with a date in the past meaning retry now.
38
+ - A stubtest step in `make check`, running mypy against the shipped type stubs so stub drift fails the gate instead of shipping. The dev extras now carry mypy and maturin on the same pins the build itself uses.
39
+ - Version coherence coverage for `Cargo.lock` in `scripts/check_version.py`, with tests that copy the repo layout, mutate one carrier of the version, and watch the guard fail. The guard also accepts prerelease version headings in this changelog.
40
+ - A Dependabot configuration proposing weekly updates for the GitHub Actions both workflows use.
41
+ - A recorded corpus conformance layer in the test suite, with 41 recorded fixtures and 4 synthesized fixtures excerpted from the live recording sessions, a corpus builder script that regenerates every fixture from its cassette, a coverage manifest and coverage table accounting for all 55 taxonomy cases, and three suites covering recorded conformance, synthesized conformance and corpus accounting.
42
+ - Three constants modules carrying the vocabularies callers pass most often, as plain string constants that drop into the existing client slots with no adapter. `placekit.types` holds the place type vocabulary from the official Place Types page (478 Table A values, 36 Table B values, 4 address component only values and the 2 Autocomplete collections), `placekit.lang` holds the 82 supported language codes from the Google Maps Platform FAQ table, and `placekit.ranking` holds the ranking preferences split by method, since Text Search and Nearby Search accept different sets. The modules are vocabularies only. The type and language slots keep accepting any string as far as vocabulary goes, including values this version has never heard of, while the ranking slots form a closed set and refuse an unknown value locally with a `ConfigError` naming the values the endpoint accepts. A string carrying control characters or unpaired surrogates is refused locally in every slot.
43
+ - A `placekit.address` helper module over the typed models, with component lookup by type, country and postal code extraction across the component and postal carrier sets, district resolution, presence flags, compound plus code splitting and postal address field readers. City resolution returns its value with the carrier it came from and a confidence grade in the open, because which carrier holds the city changes by country and the carriers sometimes disagree on the same place. Stated answers ship for Japan and the United Arab Emirates as precedences and for the United Kingdom as a contested two value answer, each scoped to the sampled locality it rests on, and every other country follows a generic fallback chain capped at the heuristic grade. Caller argument errors in the module raise `ConfigError`. The module ships no prefix or suffix postal assembly and no sorting code reader and no formatting, because the recorded corpus holds no instance those surfaces could rest on.
44
+ - Object arguments for the two calls migrants reach for after a search. `Client.place_details` and `AsyncClient.place_details` now accept a `Place` or a `PlacePrediction` as well as a place id string, returning a new `Place`, and an object carrying no id fails locally with the field mask named as the cause. `Client.place_photo` and `AsyncClient.place_photo` now accept a `Photo` as well as a photo name, and a `Photo` passed with neither size argument defaults one size from the photo itself, its native width capped at 4800 or its height when it carries no width.
45
+ - The attribute reading contract is now documented in the models and field mask documentation. An attribute the response did not carry reads None, an empty list, or its empty default, never an error and never a fabricated value, and raw membership by wire name is the supported way to tell carried fields from unreturned ones.
46
+ - A migration guide at MIGRATING.md walks python-google-places users across to placekit through the four canonical legacy examples, each executed against the offline stub, with the name and constants mapping, the attribute reading contract, and the removed endpoints named as removals.
47
+ - A String locations section in the migration guide teaches the three crossings that replace the legacy hidden geocode, under the Round 10 decision record's Option C ruling. The caller's own Geocoding API call comes first, with the v4 generation named as current, the enablement and key restriction steps stated before the code, and its dated price. The autocomplete and details crossing follows on shipped surface at its per lookup price, and the one call text search crossing closes the section with its Pro tier price and its place semantics warning at full strength. The geocode boundary itself stands, and the crossings are calls a caller performs, not a client feature.
48
+ - A normalization guide at NORMALIZATION.md teaches the address handling the recorded fixture corpus and placekit.address embody, with an oddities catalogue of the shapes the corpus actually returned, per country readings that state their evidence and its reach, the decision rules the module's grades and carriers rest on, worked recipes that run against the recorded fixtures, and the list of things the module cannot promise. A proof suite at tests/test_normalization_guide.py executes the guide's examples against the fixtures and pins its boundary sentences as text, so a later edit cannot quietly soften them away.
49
+ - A vulnerability scanning workflow for the locked Rust tree. A weekly scheduled run and any change to `Cargo.lock` audit the tree through cargo audit, and Dependabot now proposes weekly updates for the Rust and Python dependencies alongside the GitHub Actions pins, so a published advisory against the runtime surface produces a scan result and an update proposal instead of waiting on a human to notice.
50
+ - `scripts/check_wheel_sbom.py`, which fails unless every wheel in a directory carries the embedded SBOM the README promises and the document itself is sound, parsing as CycloneDX with the lockfile's component set, the workspace root package as its metadata component, and a licence entry on every component, with a step in the wheels workflow test leg running it over each built wheel.
51
+ - A standing adversarial test module at tests/test_adversarial.py, adopted from the Round 20 audit harnesses as a bounded and deterministic subset that runs in seconds. It pins the caller iterable bounds and documented maxima, the fraction and wrong type refusals, the direct layer error hierarchy and depth guard, the argument error unification and the photo content type case gate, and it carries the re-decode stability property over every shipped fixture, that decoding a result's raw again yields an equal result.
52
+ - Cargo tests at the bindings layer for the five conversions that mint an order or a protocol out of same typed parts the compiler cannot check. The city row conversion, the city resolutions lazy read protocol, the district resolution, the postal code result and the component flags are each pinned with a distinct sentinel value in every slot a transposition could trade, so a swapped field or a flipped protocol flag fails at the layer that makes the mistake instead of surfacing in a Python parity battery. The field mask construction grammar in the core gains pins for its first byte rule, the empty segments its separators make at the edges and doubled, and well formed paths past the caps the mask constructor carries, which the construction grammar does not share. The segment alphabet boundary is pinned in full as well, with a segment head admitting exactly the 52 ASCII letters and a segment tail exactly the 62 ASCII letters and digits, and every other byte value refused in a shape where that byte alone decides the verdict.
53
+
54
+ ### Changed
55
+
56
+ - The payload depth verdict behind `from_api` validation is now decided in the Rust core. The Python layer still walks each payload itself, reading through the caller container protocols in the order it always has, and sends the core a trace of the walk, one event per container visit carrying its kind and its depth. The core answers whether any visit passed the limit of 128, and the refusal keeps its message and its `DecodeError` type, so no caller code changes. The verdict's behavioural coverage lives in cargo tests over the core models module, with the Python suite kept as the parity suite over the delegation layer.
57
+ - The construction of a `FieldMask` is now computed in the Rust core. The Python class reads its arguments through the same guarded reads as before, converts the checked paths into plain data, and lets the core decide the path grammar, the duplicate dropping and the ordering of a set of paths, so the class keeps its signature and its ConfigError discipline and no caller code changes. The paths a mask stores are still the caller's own first seen objects, and the header value is still joined from them in Python. One boundary travels with the delegation. Where a caller supplied operation returns a wrong answer instead of raising, the construction follows the values as sent to the core rather than the answer the operation gave. The class's behavioural coverage now lives primarily in cargo tests over the core mask module, with the Python suite kept as the parity suite over the delegation layer.
58
+ - The address readings behind `placekit.address` are now computed in the Rust core. The Python module validates and materialises caller arguments through the same funnels as before, converts the checked components into plain data, reads each postal field at the point its reading touches it, and wraps the core answers in the same result types, so every helper keeps its signature and its ConfigError discipline and no caller code changes. One boundary travels with the delegation. Where a caller supplied operation returns a wrong answer instead of raising, the readings follow the values as sent to the core rather than the answer the operation gave. The module's behavioural coverage now lives primarily in cargo tests over the core address module, with the Python suite kept as the parity suite over the delegation layer.
59
+ - **Breaking.** `raw` is now the response body itself, on every model the client returns. The Rust core returns the body beside the typed value and Python builds raw from it, so a wire name is a key in raw exactly when the response carried that field, with no reserialization in between. Explicit nulls, explicitly empty lists, empty strings and zero valued fields now keep their keys in raw, and fabricated values are gone from it, so a location the wire sent with a latitude alone no longer claims a longitude in raw. Callers who compare raw between responses, snapshot it into golden files or branch on raw membership will see those keys appear and the fabricated ones disappear on the same wire data. In the same change, two payload shapes that used to kill a whole response now decode the way the model layer has always decoded them. A null in a plain string position reads None in the attribute, and a null element inside a modelled string list reads None at its wire index. An explicit null where a list or a number belongs still raises `DecodeError`, under the standing contract that a null is not a synonym for absence, but the error now comes from the Python decoder, naming the model and the key where the position has a decoder form and the generic conversion form where it does not, and it carries the HTTP status of the response it arrived on. A null place id on an autocomplete prediction now surfaces one call later, as a `ConfigError` from the details resolver, instead of failing the autocomplete call itself. A result raw that carries one of the still refused nulls can no longer be fed back through `from_api`, whose decoder applies the same contract to it.
60
+ - **Breaking.** StringMatch offsets are now optional. A match the API returned without a start or end offset now reports None for that offset instead of a fabricated 0, so an absent offset and a sent 0 are distinguishable again. Callers who need the old behaviour can write match.start_offset or 0. In the same release, every model class that decodes from a JSON object now carries that object on a raw attribute, matching the behaviour Place and the result classes always had. Constructor signatures gain the raw parameter last, with a default, and match args gain raw last.
61
+ - Because every raw is a deep copy of its payload, a decoded result now holds its response payload about twice over, once across the typed fields and once across the raw attributes.
62
+ - **Breaking.** Search result `routing_summaries` are typed `RoutingSummary` objects with `Leg` entries instead of untyped dicts, on both text and nearby results. The dict form stays reachable unchanged through the result level `raw`, so no response data is lost in the change.
63
+ - A circle in a Text Search location restriction now raises `ConfigError`, the same type as the other local validations, instead of a bare `ValueError` outside the exception hierarchy.
64
+ - Client construction and `FieldMask` raise `ConfigError` for empty keys, invalid timeouts and invalid mask paths instead of bare `ValueError`, so every local misuse has the one documented type.
65
+ - Default field masks now include the place resource name on each endpoint that carries a default, and the README and fields docs state plainly that the defaults reach the Enterprise billing tier.
66
+ - Header construction fails loudly on a value that cannot be sent instead of silently dropping the header, and an API key that cannot be a header value is rejected when the client is built.
67
+ - The offline test stub now honours field masks, returns autocomplete distances only for requests with an origin, and serves photos through a redirect, so the suite asserts the behaviour the real API actually has.
68
+
69
+ - Release wheels now build on native arm runners (ubuntu-24.04-arm, macos-15) instead of cross runners, with Linux wheels pinned to manylinux 2_28, and the publish job checks the tag against the manifests before uploading to PyPI through trusted publishing.
70
+ - The macOS arm64 wheel build and test legs moved from macos-14 to macos-15, after GitHub retired the macos-14 image those legs were pinned to, so the release legs still start on tag day.
71
+ - The version guard now refuses the publish path while this changelog's Unreleased section still holds entries, when no release heading exists for the target version, or when a release heading is duplicated, and it ignores headings inside fenced code blocks when it reads the changelog.
72
+ - Release wheel builds now remap compiler paths, so the native module inside a released wheel no longer embeds the build machine directory layout in the source paths its panic locations record.
73
+ - The release build tool is pinned exactly. The wheels workflow now sets the maturin version on every build action call, at the same 1.15.0 the test legs and the dev environment carry, instead of resolving whatever version the action defaults to on the day the tag is pushed. The two rust-toolchain pin comments now describe their SHA as the master branch tip it is, since that action publishes branch refs rather than versioned releases.
74
+ - The dev extras now pin the gate tools exactly, at pytest 9.2.0, mypy 2.4.1 and maturin 1.15.0, the versions the gate environment runs. They previously declared floors, so CI and a local venv each resolved the newest satisfying release at run time and an unreviewed tool release could enter the gate, stubtest included, with no diff in this repo. A tool bump now arrives as a deliberate edit to the pins, in the dev extra and the workflow legs together.
75
+ - The source distribution now carries rust-toolchain.toml, so a build from the sdist selects the pinned Rust toolchain rather than whatever toolchain the machine defaults to.
76
+ - The package metadata no longer carries a Documentation project URL that repeated the Homepage address.
77
+ - Client construction validates the base URL. A base URL carrying a query string or a fragment, or one whose scheme is not http or https, fails at build time instead of silently rerouting every request, while path prefixes keep working and dot segments can no longer escape the configured prefix.
78
+ - Client construction also rejects a base URL carrying embedded credentials, which used to become an Authorization header sent to the API host on every request without the caller seeing it.
79
+ - The Rust core no longer follows redirects automatically, so the API key header can never be resent to a redirect target. Photo media redirects are followed manually with the API key attached to the first request only.
80
+ - Field masks remove duplicate paths rather than rejecting them, reject empty segments, wildcard segments and non-ASCII characters, and are bounded at 100 paths and a 4096 byte header value.
81
+ - The native module rejects timeout values above one hour, the same cap the Python clients document, instead of only the Python layer enforcing it.
82
+ - Errors from the Rust core keep their underlying transport or decode cause as a typed source instead of flattening it into message prose.
83
+ - Decoded response values are passed through without clamping or range checks, a deliberate posture now stated in the core documentation. The client is a transport and does not hide what the API returned, and request values the caller controls are validated before sending instead.
84
+ - The dev profile keeps only line-table debug info, so incremental `maturin develop` builds stay fast, and the release profile is unchanged.
85
+ - **Breaking.** Response models are now frozen dataclasses, matching `FieldMask`, and each model's `raw` is a deep copy of its payload, excluded from repr and equality, so a shared result cannot be rebound field by field and results compare on their modelled fields. The freeze is shallow, and list fields and the raw payload stay mutable in place. `LatLng` is hashable.
86
+ - The default Place Details field mask now requests every modelled field, so a default `place_details` call populates address components, plus code, viewport, postal address and current opening hours instead of leaving them permanently empty, while the search defaults remain a curated subset.
87
+ - Printing a `PlacesError` now shows the context the type collects (the failed call, the status code, the Google status and the retry delay) alongside the message.
88
+ - `Place.opening_hours` gains the alias `Place.regular_opening_hours`, matching its wire name.
89
+ - The README now leads with the prebuilt wheel install story, and building from source stands as a full section near the top.
90
+ - **Breaking.** Text Search drops its duplicate `max_results` parameter, leaving `page_size` as the one spelling for that endpoint. Nearby Search keeps `max_results`, which maps to its own endpoint's `maxResultCount` field. Passing `max_results` to `text_search` now raises a plain `TypeError`, like any unknown keyword.
91
+ - Every GitHub Action in both workflows is pinned to a commit SHA with the resolved version in a trailing comment, including the PyPI publish action, which previously tracked a moving branch. The workflows declare per-job permissions and no longer persist checkout credentials.
92
+ - Release builds no longer target the retired macos-13 runner image, which had left the macOS Intel wheel leg unable to start, and the macOS legs now pin their deployment targets, 10.12 for Intel and 11.0 for Apple silicon.
93
+ - Publishing is gated on an install matrix that installs and imports every built wheel on its own platform plus the sdist, with a Python 3.10 leg exercising the abi3 floor, and every test install is hermetic, so a job can never silently test the published package instead of the artifact it just built. The publish job runs only on version tag pushes, the tag pattern accepts version tags only, and a publish in flight is never cancelled by a re-run.
94
+ - The declared Rust floor is 1.88, the oldest toolchain that compiles the locked dependency set, up from a declared 1.74 that the locked dependencies no longer support. The pin lives in a new `rust-toolchain.toml` and in CI, and Cargo and maturin invocations in the gate and the workflows now run with `--locked` wherever the flag is accepted.
95
+ - Package metadata uses the SPDX license expression form, carries project URLs, Python classifiers for 3.10 through 3.14 and the operating system classifiers for MacOS, Windows and POSIX Linux, matching the wheel platforms, and aligns the keyword list with the Cargo metadata.
96
+ - The source distribution no longer ships the CI configuration under `.github`.
97
+ - **Breaking.** A plain http `base_url` is refused unless the parsed host is loopback. Both key forms, the header and the photo key query, travel over the base connection, and plaintext exposed them to anyone on the path to a network host. Loopback bases keep working because that is the documented local stub shape, and classification reads the parsed host, so a hex or decimal IPv4 spelling is judged as the loopback address it canonicalises to.
98
+ - **Breaking.** Photo media redirects are screened before each hop is followed. A target carrying userinfo is refused, an https target naming a loopback host the base does not share or a private, link local or unspecified IP literal is refused, and a plain http target is followed only from a loopback base to a loopback target. A media response can no longer steer the client at internal network addresses or downgrade it to plaintext.
99
+ - **Breaking.** Photo bodies are accepted only in the image formats the client verifies by signature, JPEG, PNG, GIF and WebP. Every other declared type is refused as a decode error, including `image/svg+xml`, an XML format that can carry script, which previously passed unchecked.
100
+ - **Breaking.** An error response whose body is not a Google error envelope no longer has its body quoted in the exception message. The message carries the HTTP status and the body size instead. Envelope errors keep their parsed message and attributes unchanged.
101
+ - The concurrency and lifecycle contract is now stated in the README and in both client docstrings. Sharing one client across threads, tasks and event loops is a documented promise, the close docstring now says plainly that closing gates later calls and neither cancels nor waits for in flight work, and the pooling posture, the process lifetime runtime, the cancellation behaviour and the limits on sharing results and on subinterpreters are written down where callers can find them.
102
+ - **Breaking.** Every iterable a caller hands to a client method is now materialised under a bound before any request is built. Where the API documentation the client already states gives a slot a maximum, that maximum is the bound, so Autocomplete primary types stop at 5 and the Nearby Search type filters stop at 50, and an Autocomplete collection such as `(cities)` may appear only as the sole primary type value, as the types documentation has always stated. Every other slot falls under one named universal ceiling of 1000 items, shared by the field mask builder and the request body walk. An endless iterable now refuses promptly with a `ConfigError` on both clients instead of hanging the call, an iterable that raises while it is being read surfaces as a `ConfigError` with the original exception chained, and a list longer than its bound raises `ConfigError` before any request instead of travelling to the API to be refused there.
103
+ - **Breaking.** Argument validation on the direct argument methods now matches the body methods. A non-string place id, language, region or session token passed to `place_details` raises `ConfigError` instead of escaping the native conversion as a bare `TypeError`, and the same holds for the photo name on `place_photo`. Language and region codes and their kin refuse control characters and unpaired surrogates at the Python layer with a `ConfigError`, instead of reaching the wire percent encoded or escaping as a bare `UnicodeEncodeError`, and a caller object whose own methods raise while its arguments are being validated or materialised now surfaces as a `ConfigError` with the original exception chained instead of the bare builtin it raised.
104
+ - **Breaking.** The model conversion layer now validates the scalar types it declares. A fractional number in an integer typed position raises `DecodeError` instead of being silently truncated, through the client and in the direct `from_api` layer alike, while exactly integral floats and the digit strings the wire itself sends still decode normally. A wrong JSON type in a declared string slot raises `DecodeError` instead of being stored verbatim, and direct `from_api` misuse that used to escape as a bare `AttributeError` or `TypeError`, including a mapping handed to a string list slot being read as its own keys, now surfaces as a `DecodeError` naming the model and the key, with payload nesting past a stated depth guard refusing as a `DecodeError` instead of a bare `RecursionError`. The standing null settlements are unchanged by this. A null in a string position still reads None, and a null element inside a modelled string list still reads None at its wire index.
105
+ - **Breaking.** The refusals above are complete at their edges. A caller mapping whose item stream cycles over keys it already yielded now counts against the materialisation ceiling by items consumed and refuses with `ConfigError` instead of hanging the call. A fractional `Decimal` or `Fraction` in an integer typed position refuses with `DecodeError` the way a fractional float already did, and a huge integer in a float position on the direct `from_api` layer refuses with `DecodeError` instead of escaping as a bare `OverflowError`. Payload objects that misbehave through the standard protocols, a values or iteration method that raises or a conversion that raises, surface as `DecodeError` on the model layer the way caller objects already did as `ConfigError` on the client, and `BaseException` subclasses still pass through every guard untouched. The items of the code carrying list slots, region codes and the type filters and price levels, pass the same control character and lone surrogate validation as the scalar code strings. A malformed hand built address component, one whose types value is not a list or a plain mapping standing in the components list, raises `ConfigError` from `placekit.address` instead of a bare `TypeError` or `AttributeError`.
106
+ - **Breaking.** Error envelope classification no longer depends on the whole response body parsing. An envelope whose body runs past the error body read cap, or that carries a value nested past the JSON parser depth limit anywhere in the body, used to lose its Google status, its message and its error code and fall back to the bare HTTP class, so a quota error on an HTTP 400 arrived as a plain API error and an invalid argument error on an HTTP 429 arrived as a rate limit. The core now recovers the envelope head fields with a tolerant scan whenever the whole document parse fails, and classification follows the envelope in those cases, with the details reading empty when they cannot be parsed. Well formed bodies under the cap behave exactly as before. The same change maps the `OUT_OF_RANGE` status to the invalid request class directly, so that class no longer depends on the response arriving on its canonical HTTP 400 pairing.
107
+ - **Breaking.** A non finite number in a payload float position now raises `DecodeError` naming the model and the key, through the client and in the direct `from_api` layer alike. An infinite or NaN latitude used to decode until the `LatLng` construction check refused it with a `ConfigError`, a caller misuse type, so a malformed payload escaped the decoder contract as the wrong exception. Every float slot now honours the contract, coordinates and ratings and distances alike. Building a `LatLng` by hand with a non finite value still raises `ConfigError`, because that is caller misuse rather than a payload.
108
+ - **Breaking.** A timeout anywhere in a call now surfaces as a `NetworkError` naming the timeout, including a timeout that fires while an error body is still streaming, which used to arrive as the bare HTTP class with an unreadable body note and no timeout token in any attribute. Timeout originated network errors also report `retryable` as true, matching the documented meaning of the flag. Other network errors keep their existing retryable readings.
109
+ - The remaining scalar request slots now validate in Python the way their neighbours already did. `page_token`, `rank_preference` on both search methods, `included_type` and the Autocomplete `session_token` pass through the code string validator, so a wrong type or an unpaired surrogate raises `ConfigError` naming the argument instead of failing inside the native conversion, and unpaired surrogates in the free text arguments raise `ConfigError` naming the argument instead of escaping as a codec error. No vocabulary validation was added, and the client keeps accepting any code string in these slots.
110
+ - API exceptions no longer chain the native core exception as their cause. Every native attribute is already copied onto the Python exception, so tracebacks now show the one exception a caller actually catches instead of rendering the same message twice, once under the private native class name.
111
+ - `PlacesError` construction normalises `retry_after` the rest of the way. A bool or a non numeric value now reads as None instead of passing through untouched, so the stored value is always a float or None.
112
+ - Address helper failures and field mask construction failures now carry context. An exception from a `placekit.address` helper names the helper in its context, and a `FieldMask` construction failure carries the mask as its context, matching the README statement of which surfaces carry context. Model decode errors carry no context of their own and name the model and the key in the message instead, and a client call attaches its context to them as it does to every error it raises.
113
+ - String values inside parsed error details are now capped at 500 characters, the same bound the error message has always had, on every envelope path. A details entry of 100000 characters no longer reaches the exception whole.
114
+ - The core configuration error rendering drops its fixed prefix and carries the specific message alone, and an error headline no longer renders an empty gap or a duplicated HTTP status when the response carried no Google status.
115
+
116
+ - Response decoding now pays for copies only where a copy carries an invariant. Through a client call the model layer reads the typed value the bindings built for that call in place instead of deep copying it at every model node, adopts the parsed body as the top level raw instead of copying it, and copies raw once per node below the top, which is the copy that keeps one node's raw independent of every other node's. The direct `from_api` layer makes one copy of a payload per node where it used to make two for every node below the first. No result changes. Raw at every level still deep equals the wire subtree, a result still shares no mutable state with the caller's dict or with any other node's raw, and new tests pin the isolation in both directions on both layers beside a census that holds the decode to one deep copy per node. In the same change, `placekit.address` validates and scans a place's components once per public call and shares that work across every reading the call composes, where one `address_flags` call used to revalidate the same list seventeen times over.
117
+ - **Breaking.** The typed layer no longer fabricates coordinate values the wire did not send. A location carrying only a latitude used to expose a longitude of 0.0 in the typed result while `raw` beside it carried no longitude, an empty location object used to read as a place at the zero coordinate point, and a viewport missing a corner used to expose that corner as a `LatLng` of 0.0 and 0.0. The core now carries absence through the typed dict it hands Python, so those bodies behave like their explicit null counterparts: a partial location or a viewport missing a corner raises `DecodeError` naming the missing key, and an empty location object reads as no location, exactly as a null location does. Internal core reads keep the lenient 0.0 default, coordinates the wire actually sent as 0.0 are untouched, and `raw` is unchanged. Request validation also refuses a coordinate that is absent altogether, on the same ground it already refused an explicit null.
118
+ - Core decode failures now name the field that failed. A wrongly typed documented field still refuses the whole call as a `DecodeError`, under the standing settlement, but the message carries the serde field path in wire names with the element index for list members (for example `places[1].rating`), matching the payload key quality of the Python layer's own refusals, instead of naming only the expected type and a line and column. No partial result and no raw body is returned on such a refusal, as with every other `DecodeError`.
119
+ - An error response body larger than the error read cap is no longer reported with the capped read length as its size. The message states that the read stopped at the 262144 byte cap and the full size is unknown. Bodies read in full keep the exact size form.
120
+ - **Breaking.** The `placekit.address` helpers now refuse caller shaped corruption instead of answering through it. A component list, a `Place`, a postal address or a plus code argument carrying a value of the wrong type in a field a helper reads (a component text or types item, a postal code or locality or region code field, a formatted address) now raises `ConfigError`, where the same inputs used to escape as bare `AttributeError` or `TypeError` failures or, worse, flow into a result under a type it does not have, so an int postal code no longer comes back as an int and an int region code no longer crashes the routing read. A non iterable components argument raises `ConfigError` rather than a bare `TypeError`. In the same change, one shot iterables now answer correctly. A component list held as a generator used to be consumed by validation and then read empty by the scan, so every helper silently answered as if the place had no components, and a `Place` carrying its components as a generator read empty through every Place taking helper. Each call now materialises its components once under the bounded ceiling and answers from the contents that call carried.
121
+
122
+ ### Fixed
123
+
124
+ - A container the materialisation ceiling walk did not count could carry its members past the ceiling whole inside a free-form dict body. The walk counted mappings, lists and tuples, but the converter materialises any sequence at the list-typed fields, so a set, a frozenset, a deque or any other sequence handed to such a field (for example the connector types inside `ev_options`) reached the wire at any size, past the ceiling of 1000 values the same field enforces for a list. Every container the converter can materialise now counts toward the budget by the same rule, at the same boundary and at any nesting depth, and a container inside the budget still converts and reaches the wire as before.
125
+ - The photo redirect screening and the base URL parsing now judge the translation spellings of an IPv4 address by the address they embed, where they used to judge only the IPv4-mapped and IPv4-compatible spellings. The NAT64 well-known prefix `64:ff9b::/96`, the NAT64 local-use prefix `64:ff9b:1::/48` and the IPv4-translated prefix `::ffff:0:0/96` each passed the redirect screen unjudged, so a spelling of a private or loopback address in one of those forms was followed like a public address instead of being refused like its dotted spelling, and a base URL read the same spellings the same wrong way. The embedded address recovery now reads all three translation prefixes, reading the local-use prefix at both of its embedding positions, the RFC 6052 layout for that prefix length and the last 32 bits, because a screen cannot know the prefix length the network is configured with, so a translation spelling of a non-public or loopback address is refused wherever its dotted spelling is, and a translation spelling of a public address is still followed. A local-use spelling whose two positions disagree, one reading as the unspecified address and the other as a public one, is refused as well, because either reading of it may name the local host.
126
+ - The README spelling guard in the test suite now reads the text a reader of the rendered page sees. A keyword, a FieldMask member or a price level value spelled only inside an HTML comment, a hidden element, a script or style element, a tag attribute, a link destination or a link definition line counted as taught under the raw read, so a README could pass the guard while teaching the name nowhere a reader can see it. Those spans are removed before the checks read, with fenced code examples left untouched because an example renders as visible text, and the shipped README carries no spelling inside any removed span, so no teaching changes.
127
+ - The client refusals that name a caller supplied value now render those values through the library's guarded forms. The place id and photo resolvers formatted the values their messages name with raw reads, a raw type name read in the place id message and raw reprs in both, and every other argument validator that names a caller value shared the shape, so a value whose repr raises, or whose type's name read raises, detonated the refusal's own formatting and the caller received only the argument wrap's generic wording, which names the wrong failure, where the seat's own documented explanation belonged. Those seats now render the name through the guarded name reading and the values through the safe repr, and the refusal chains the original raise the raw read produced, so a hostile value surfaces as the seat's own ConfigError carrying its own explanation, with the guarded fallback form standing where the raw read stood. Every ordinary message renders byte identically to before. One wording change is intended rather than incidental. A type name that answers as something other than a plain string now stands in the fixed unrepresentable form in the place id resolver's message, instead of the non string answer being printed as if it were a name.
128
+ - The core error documentation no longer claims the fallback message echoes the response body. The docstring on the core error type's response constructor said a body with no usable envelope field at all falls back with the body echoed as the message, where the shipped fallback has not quoted an unparsed body since the no-quoting settlement and instead renders a body carrying no content as no error body and reports only the size in bytes of a body carrying content, or the fact that its read stopped at the cap when the read was capped, so the sentence now states that behaviour and nothing a caller can observe changes.
129
+ - **Breaking.** The refusals that report a failure a caller supplied value raised, and the string form of every placekit exception, now render the values they name through a guarded conversion. An exception whose own repr raises no longer detonates the refusal that reports it and escapes bare through the exception constructor, `FieldMask.of`, a client call or an address helper, and a message or context value whose own formatting raises no longer breaks the string conversion of the exception carrying it, the conversion logging and printing both run. The repr form of an exception reads its class name through the same guarded reading and renders the string form through the guarded repr reading, standing in the fixed unrepresentable form where the name cannot be read as a plain string, so a subclass whose metaclass breaks the name read cannot break the repr of its own instances, and neither can a stored string message whose own repr raises. Those shapes now surface as the documented `ConfigError` with the original exception chained, the failure named through a guarded form that names its type and stands in a fixed unrepresentable form where even the type name cannot be read as a plain string, and an exception built with a non string message renders the message as its text instead of raising a bare `TypeError` from its own string conversion. A routing mapping whose items method itself raises now surfaces under the mapping copy wording naming the parameter instead of the argument wrap wording that used to catch it one level up. Every ordinary message renders byte identically to before.
130
+ - The extension stub now states the integer widths at the three slots it declared as plain int. The rank in a mask sorted order key and the kind tag in a payload depth trace are read as unsigned 8 bit values, and the depth in the trace is read as an unsigned 32 bit value, so a rank or a kind outside 0 to 255, or a depth outside 0 to 4294967295, raises OverflowError before any judgement happens. No exact stub type states those widths without misdescribing what the bindings accept, so the stub carries them as prose beside the two declarations, and new pins hold the runtime at each boundary in both directions. The prose also names the narrower values the Python layer itself mints at the rank and kind slots, which never leave 0 and 1. Nothing about the runtime itself changes.
131
+ - The corpus accounting linkage guard now reads the handler seats of a try where they stand as plain names, and a raised path whose error no named handler could catch no longer rejoins the walk after the try. Where every handler seat of a try is a plain name or a tuple of plain names, a path ending in a raise, or in the failure of an assert, standing in the body of the try, whose error stands under a name no handler names, leaves the try as the raised path it is, for the regions outside the try to carry in their own terms, exactly as the same end behaves standing outside a try, and a path ending in a raise, or in the failure of an assert, standing in the else arm of the try leaves it on the same terms with no name read at all, because an error raised in an else arm is never caught by the handlers of its own try, on any name, the else arm standing outside the region those handlers guard. Before this change the try join dropped a raised path of every kind where it stood and never read the errors its handlers named, so a test could stand a failing assert or a raise behind a handler naming a different error and still earn credit for a fixture its loading call after the try ran on no path the error took. A handler seat that is not a plain name or a tuple of plain names, a bare handler, and a seat naming one of the base error classes each keep the shipped join whole for a path of the body, as does a raise whose expression carries no statically plain name, so the change reaches only the seats a static reading can judge by name. For a path of the else arm no seat is consulted at all, because no handler of the try could catch its error whatever the seats name. A raised path arising inside the body reaches that judgement whole, because the joins it meets inside the body no longer end it where they stand. A conditional arm and a loop hand such a path to the enclosing block on the terms their walks state, and a path a try has already judged and sent out of itself crosses the regions outside it on the record of that judgement. A nested with standing in the body whose one manager stands as a suppress call under plain names resolves only the paths whose error it names, and hands a path whose error it could not suppress onward unresumed for the try to judge. That name reading now stands at a with on every seat, inside the body of a try and outside one alike, because a raised path resumes at runtime only after the region that actually suppresses it, so a test could stand an inner with whose manager names a different error than the path raises, inside an outer suppressing with, and still earn credit for a fixture its loading call after the inner with ran on no path the error took. A path a try has already judged and sent out of itself keeps the conversion at a with standing outside the body of a try, because no judgement seat stands beyond that with and the conversion is what seats the path in the join of the block the with stands in, while every manager shape that is not a suppress call under plain names keeps the conversion whole on every seat, as does a raised path whose error carries no statically plain name. A raised path of a handler body leaves the try statement outright, because a handler never catches an error its own body raises and neither does a handler standing beside it. A raised path of a match arm reaches the regions outside the match whole as well, because the join at a match now hands such a path to the enclosing block on every seat, inside the body of a try and outside one alike, its plain name travelling with it, so a try judges it by its seats, a with resumes it, and the raised check of a class body reads it, exactly as a raise or an assert standing in the block itself is carried. Before this change the match join dropped a raised path of every arm where it stood, so a test could stand a failing assert or a raise in the arm its subject takes and still earn credit for a fixture its loading call after the match, or after a try holding the match, ran on no path the error took. A raised path of a class body now reaches the regions outside the definition whole as well, because the class branch hands such a path to the enclosing block as the raised path it is, exactly as it hands the fact of the raise to its own check, so a try judges it by its seats and a match hands it onward with its arm paths. Before this change the branch emitted no path, so inside a try the handler paths carried the credit after the try for an error no handler catches, and inside a match the path where no arm fits carried it. An assert standing in a class body keeps the inert reading the shipped walk gives it. A conditional whose test stands as a constant now walks only the arm the constant selects, on the same constant reading the walk gives the test of an assert and the test of a while, and a raised path of the selected arm leaves the statement as a raised path of the enclosing block, as one standing in the block itself does. Before this change the walk never evaluated the test of a conditional, so the arm a constant rules out could carry the credit after the conditional for a fixture its loading call ran on no path at runtime. A test no constant reading settles keeps both arms and their join whole. Outside the body of a try the conditional and the loop named here keep the treatment the shipped walk gives them. The change is confined to the guard in the test suite and its round 62 pins, and nothing a caller of the library can observe changes.
132
+ - The corpus accounting linkage guard now forks its walk at an assert statement standing in a with body. Where the test of such an assert fails, that path ends where the statement stands, as a raised path of its own kind beside the path where the test holds and the body walks on, and a suppressing manager resumes the ended path after the block on the same terms as a raise, so a fixture loading call standing after the assert inside the body earns credit only when every path through the block loads the fixture it claims. Before this change the walk scanned the test of an assert and walked on with the path alive whatever the test held, so a test could abandon a suppressing with body on a failing assert and still earn credit for a fixture its later loading call ran on no path the failure took. An assert whose test stands as a true constant forks nothing, because such an assert fails on no path. A false assert standing under a manager that does not suppress it now reads as the same assert standing in the block with no manager over it reads, and the raise standing in its place reads as a bare raise reads, because a manager that cannot suppress an error is transparent to the path that raises it and the path leaves the with to be carried by the regions outside it. The change is confined to the guard in the test suite and its new pins, and nothing a caller of the library can observe changes.
133
+ - The cargo audit workflow now audits. The RustSec action builds cargo-audit from source with the cargo it finds, and the dependencies of that tool outgrew the pinned 1.88.0 toolchain the library builds with, so every recorded run failed while building the tool and no run ever read the lockfile. The audit job now scopes a newer toolchain to itself alone for the tool build and the audit run, leaving the library pin and every other job untouched. The same landing dispositions the two live advisories against the locked PyO3 0.23.5 in a new `.cargo/audit.toml`, each ignored on a written reach ground and nothing broader ignored beside them. Neither affected function, `PyString::from_object` for RUSTSEC-2025-0020 nor `PyCFunction::new_closure` for RUSTSEC-2026-0177, is called anywhere in the workspace sources or in pythonize 0.23.0, the only other PyO3 consumer in the lock, so the vulnerable paths sit inside the compiled dependency with no caller that can reach them. Any advisory not named in that file still fails the audit.
134
+ - The README endpoints paragraph now names the full filter surface it promises. Text Search gains `include_pure_service_area_businesses` and `include_future_opening_businesses`, Nearby Search names its four type filters and its own future opening switch, and Autocomplete names `included_region_codes`, `include_query_predictions`, its location shapes and its two include switches, where the clauses before named only part of what the signatures accept. The paragraph also teaches `region`, which all four read calls accept and no passage named before, and it states the `price_levels` value shape, the five `PRICE_LEVEL_` values a filter takes and a place carries on its `price_level` attribute, which the document advertised without ever spelling. The local build example in the release passage now gives glibc 2.39 as the current Ubuntu figure in place of 2.34, which no supported Ubuntu carries. New pins derive the keyword sets from the client signatures themselves and fail if a keyword on the four read calls is ever left unnamed in the README again.
135
+ - **Breaking.** A payload whose own operations raise during a direct `from_api` decode no longer escapes as the bare exception it raised. A `get`, a membership test, an index read, a truthiness evaluation at a child gate, a raw source copy, or a value repr that raises now surfaces as a `DecodeError` naming the model and the key in a could not be read form, chained from the original exception, and a raising float conversion inside the `LatLng` construction guard surfaces as its `ConfigError`. A member of the library's own error family raised by one of the wrapped operations is the exception to the conversion. It surfaces as itself, with its class, message and chain untouched, because a decode never reclassifies the library's own verdicts. The repr in the list is a rendering step rather than a wrapped operation, so a family member raised by a repr is answered by the unrepresentable form while the `DecodeError` surfaces. `BaseException` subclasses still pass through every guard untouched, and no honest payload changes any stored value, refusal message, or read, as new pins counting the reads per entry point hold. A caller who handed over a mapping with raising operations and caught the bare builtin now catches `DecodeError` instead.
136
+ - The corpus accounting linkage guard now resolves each local name handed to a fixture loading helper against the binding in effect at the loading call, walking the test body in program order, so a test that binds a claimed fixture name to a local and rebinds the same local to another fixture before the call no longer earns credit for the fixture it never opens. Bindings and credit combine conservatively where control flow forks. A name bound inside a conditional arm, a loop body, a match arm, or a try handler keeps a resolved binding after the region only when every path through the region ends holding the identical binding, counting the untouched arm of a conditional, the path where no arm of a match fits, and the path where a loop body never runs as paths of their own, and a walrus binding inside an expression applies at its position in the walk. Credit joins by intersection over the same paths, so a fixture counts as loaded only when a loading call resolving to it runs on every path through the region, and a call in an arm, a handler, a match arm, or a loop body that may never run earns nothing, while a loop over a literal sequence with at least one element, or a while loop on a true constant, whose body carries no break, continue, return, or raise that could bypass its calls runs its body on every path and keeps the credit its calls record. The state a loop leaves behind is the state the interpreter leaves, so the fall through end of a loop over a literal sequence is taken from a final pass over the body with the target narrowed to its last element, a name the body derived from the target ends holding its last pass value, and a loop whose body carries a bypass, or whose iterable is not an ordered literal, leaves the target and the names derived from it unresolvable after the loop. The loader set the guard trusts is derived to the same standard, through an admission step that refuses a definition whose own body holds a yield outside a nested definition, because calling a generator function runs none of its body, a definition carrying a decorator, because a decorator may replace the function at definition time with a callable the guard cannot see, and a definition whose name a later module level statement rebinds, in a statement or an arm of one or in the head of a compound statement, the head bindings being the target of a with item and the names a match pattern captures, because the name a caller reaches then denotes the later binding rather than the body under proof, and a definition whose name an execution the module level reaches after it rewrites, the write sets gathered from every module level definition, from the definitions standing in class bodies gathered beside them under their own names on the same terms, and from the construction methods of every module level class wherever they stand, the dunder prepare method beside the dunder new method and the dunder init method, each designated method counting where it stands as a definition anywhere in a class body on the statement terms the body is read on and where an assignment in a body binds its name on the same terms, directly or paired by an unpacking assignment with the element standing at its own place in a tuple or list value of the same length, to a plain name resolving through the module level definitions or to a lambda read where it stands, and all three inherited through the module level base classes a class names, each name resolving through the module level assignment bindings to the class it denotes to the end of the chain, the bindings gathered on the statement terms the assignment pair collection reads a body on, a plain assignment, an annotated assignment carrying a value, a walrus binding standing in an expression, and a loop target standing in a loop header alike, the value standing behind a loop target being the iterable the header walks, a name a loop target binds denoting an element of that iterable at every seat that resolves a name to a value, the class turning points, the instance resolution and the callee expansion of the reach, and the manager and iterable positions among them, and a subscript standing on such a name among them, the name standing in the subscript's value position bare or yielded to that position through an arm of a conditional expression, an operand of a boolean expression, or the value a walrus expression yields, or standing behind a further name bound to such a yielding form, the subscript selecting out of the element the name denotes beside the module bindings route on the placement index terms, the elements read where the iterable stands as a literal tuple or list or as a plain name the bindings resolve to one, a name an unpacking loop target binds denoting the component standing at its own place in the element it unpacks, paired on the same terms, and the elements of an element standing behind a loop target name a further loop walks, so a chain of loop targets resolves at every level, an assignment unpacking a single tuple or list target against a tuple or list value of the same length pairing each name it binds with the element standing at its place, recursively where an element itself unpacks, a name bound to a class name or to a subscript of one counting as that class wherever a name is turned into a class, a call naming a class counting as a call into its construction and into the dunder call method of the metaclass it stands under, counted at the call and not at the creation of the class, a plain call resolving its callee name through the module level assignment bindings standing behind it, a name bound to a plain name counting as a call into the definition standing at the end of the chain of such bindings, its body and write set read on the terms of a direct call, and a name bound to a lambda counting as a call into the lambda body read where it stands in the binding, a name bound to a subscript selecting a callable out of a literal standing in the binding or denoted by a plain name standing in it counting as a call into the callable the selection yields, a definition name so selected counting as a call into that definition and a lambda so selected as a call into its body read where it stands, and a name bound to a conditional expression, a boolean expression, or a walrus expression counting as a call into the callable the value it yields denotes, the name guard against a name resolving through itself standing over the resolution, a call naming an instance counting as a call into the dunder call methods of the class the instance stands in, its own or inherited through its base classes, the name resolved through the bindings to the instantiation it denotes, following a name bound to a further name to its instantiation in turn, or bound to a conditional expression, a boolean expression, or a walrus expression whose yielded values denote instances in turn, or bound to a subscript selecting an instantiation out of a literal, the selection composing through a further subscript standing behind it on the same terms, and each decorator standing on a definition or a class in an executing position in bare name form counting as a call into the value the name denotes, a definition name to its body, a class name to its construction and to the dunder call method of the metaclass it stands under, and an instance name to the dunder call methods of the class it stands in, and a with statement standing in an executing position counting as a call into the dunder enter and dunder exit methods of the class whose instance its manager position denotes, an inline instantiation, a name the bindings resolve to one, an instantiation an arm of a conditional expression standing in the position evaluates to, or the instantiation a walrus binding standing in the position binds, an instantiation standing as an operand of a boolean expression standing in the position, or an element of a tuple or list a subscript standing in the position selects, the literal standing in the subscript or denoted by a plain name standing in it through the bindings, or by a name bound to a subscript selecting it out of a further literal so denoted through the bindings, the selection composing through a further subscript standing in the position on the same terms, the methods gathered on the same installation terms through the base classes the class names, and a for statement standing in an executing position counting as a call into the dunder iter method of the class its iterable position denotes on the same widened resolution and into the dunder next method of the same class where the gathered iter method returns the instance itself, standing as its parameter name alone, through a name its body binds to its first parameter, the binding standing as an assignment to a value counting in any of those forms, as an unpacking assignment pairing the name with an element bound to the parameter, as a plain loop target paired with an element of a literal holding the parameter, or as a conditional expression either of whose arms is such a name, through a walrus expression yielding it, as an operand of a boolean expression yielding it, or through an arm of a conditional expression returning it in any of those forms, or as an element a subscript selects out of a literal tuple or list holding it, the literal standing in the subscript or denoted by a name the body binds to one, directly or through an expression yielding one on the same terms, an arm of a conditional expression, an operand of a boolean expression, the value a walrus expression yields, or the selection a subscript performs, a selection selecting a name the body binds to one counting as the literal that name denotes in its turn, the name followed through the body's own literal reading on the same terms, or standing in the subscript as the value a walrus expression yields, as an operand of a boolean expression, in an arm of a conditional expression, or behind an inner subscript selecting it out of a further literal, the element read on the index terms the placement reading selects on, and into the dunder getitem method of the same class where no dunder iter method stands in its chain, the sequence fallback the statement performs in the iter method's absence, and a subscript whose value position resolves to a module level class, a conditional expression standing in the value position resolving to the classes of both its arms, a boolean expression resolving to the classes of its operands, and a walrus expression resolving to the class of the value it yields, and a subscript standing in the value position resolving to the classes of the elements it selects out of a literal, the literal standing in the inner selection itself or yielded there by an arm of a conditional expression, an operand of a boolean expression, or the value a walrus expression yields, or denoted there by a name bound to a subscript selecting it out of a further literal so standing, and a name standing in the value position bound to a subscript whose own value position holds such a yielding form or a loop target name, or bound through such a yielding form to the value the form yields, a name so yielded resolving on the same terms in turn, or bound to a subscript whose own value position holds a name bound to such a yielding form, the yielded loop target name standing in the value position on the same principle, or bound by plain assignment to a name bound through such a yielding form in turn, or bound to a subscript whose own value position holds a name bound by plain assignment to a name bound to such a yielding form in turn, a yielding form standing between the plain assignment and the subscript behind it on the same principle in either case, resolving beside the denotation the bare name takes to the classes of the elements that subscript's selection yields on the same terms, counting as a call into the dunder class getitem method of that class wherever the reach reads the subscript as an execution, and an augmented assignment standing in an executing position counting as a call into the in place method its operator names, the dunder iadd method for the additive operator, the dunder isub method for the subtractive, the dunder imul method for the multiplicative, and their siblings for the remaining operators, of the class the value its target name denotes stands in, or into the plain method of the same operation of that class where no in place method stands in its chain, and a class definition standing in an executing position counting as a call into the subclass hooks of the module level bases it names, resolved through the base chain to its end, a base standing in a subscript counting as the plain name standing in its value position in that reading and in every base reading beside it, each hook counting the names its body assigns under a global declaration, and, when its metaclass keyword names a module level class, as a call into the construction gathered for that name, as a call into the dunder set name methods of the descriptor classes whose instances stand assigned in its body, placed there on the statement terms of the body itself, by an assignment standing directly in the body or in an arm of a compound statement among its statements, by an element of a tuple or a list the assignment unpacks, the tuple or list standing in the assignment or denoted by a plain name standing in it through the bindings, by a subscript selecting an element out of a tuple or a list standing in the assignment or denoted by a plain name standing in it through the bindings, or by a name bound to a subscript selecting it out of a further literal so denoted through the bindings, the selection composing through a further subscript standing as its value on the same terms, by an instantiation standing in an arm of a conditional expression the assignment evaluates, by an instantiation standing as an operand of a boolean expression the assignment evaluates, by an instantiation whose callee stands in a subscript naming the class in its value position, or by a plain name the module level bound to an instantiation of the class, to an arm of a conditional expression, to an operand of a boolean expression, to an element a subscript selects, or to the value a walrus expression yields, or a loop target bound to an element of the iterable it walks where an element instantiates the class, their own or inherited through the module level base classes they name, resolved through the base chain to its end, and, when its keywords name no metaclass, as a call into the construction gathered for the most derived metaclass its bases stand under, counted for every candidate the base chains leave unordered between themselves, the dunder prepare method of the metaclass on either path standing in the construction gathered for it beside the dunder new and dunder init methods, so the names it assigns under a global declaration are counted with the construction and its body is read with theirs, and the reach read from the statements standing after the definition, counting the bodies of classes standing at module level among the executing statements and closing over the calls those statements make into the bodies of the definitions they call and the construction methods of the classes they construct, reading beside those calls the parts of a definition that evaluate as its statement runs, its decorator expressions and its default values and its annotations, and the parts of a class that evaluate as its statement runs, its decorator expressions and its base expressions and its keyword values, in any executing position, the body of a class standing inside a body the reach has entered read on the same terms as the body of a class standing at module level, a class body running at its statement, while the body of a definition standing there stays deferred, and the body of a lambda a statement invokes on the spot where it stands, the refusal keying on the execution, since an installer no execution reaches leaves the name denoting the certified body, because the name a caller reaches then denotes what the execution leaves behind rather than the body under proof, and a helper joins only when a single analysis of its body, with every one of its parameters bound to its own distinct symbolic fixture name, credits exactly the symbolic name of one parameter on every path that completes, with the paths that end in a raise counted beside them, so a candidate is refused when a raised path holds credit for anything but that one symbolic name, because a caller that suppresses the raise runs on whatever the path loaded before it ended, and a loading call in such an analysis credits only the argument standing in the name slot of the loader it calls, never an argument in any other slot. A helper whose own body reads a file from disk is proven in the same terms, its reads standing in for loading calls and crediting the fixture names their targets may denote, so a reader that diverts the name it is handed to another fixture, or reads its argument as a decoy and returns a fixed name, is refused beside a helper that drops its argument, loads a fixed name of its own, hands its argument over on only some of its paths, or feeds the name slot of a loader from different parameters on different paths. A loading call inside a nested function definition or a lambda earns no credit, because such a body runs only when it is called and a call into it is one the guard cannot see into. A name a nested definition declares global or nonlocal reads as unresolvable from the definition onward, because a call into the definition can rewrite that name in the enclosing scope behind the guard's back. A return ends its path where it stands with its credit frozen toward the whole test, a raise, a break, or a continue ends the path of the block holding it, and the statements after any of them in the same block are dead and earn nothing, while a try carries a raised path onward through its handlers and a with body forks like any other region, its raised paths resuming after the with in case a context manager suppresses the exception. That with model covers abandonment by a raise statement, and abandonment by an exception raised while an expression in the body evaluates, silently under a suppressing manager, stands as a named residual outside the guard's static contract, because whether such an expression raises can turn on data the test source does not carry. The expression scan follows the same evaluation rules, so a comprehension body earns credit only on a provably nonempty literal sequence, a generator expression body earns none, the right operand of a boolean operation earns credit only behind constants that guarantee its evaluation, and a conditional expression joins its branches by intersection. A class body walks in a forked environment whose bindings are discarded when the definition ends, so its assignments stay in the class namespace while a loading call written in the body still earns credit. The Round 52 red team found the evasions across thirty passes, in the earlier order blind analysis, which collected the constants of every assignment to a fed local, then in the first repair, which walked arms and loop bodies as if they always ran and never applied walrus bindings, then in the second, which joined bindings across regions while credit stayed a single shared set that any walked call could write into, then in the third, which joined credit by path and never walked a deferred body, while modelling no path ending, no expression evaluation, and no class scope, then in the fourth, which left a loop target holding the whole of its literal iterable after the loop and trusted that a helper hands over the name it was given, then in the fifth, whose faithfulness proof could be passed through a parameter carrying no fixture name, which treated a nested definition as inert while a closure wrote back through it, and which left the disk reading seed of its derivation standing on a dependence check no proof had tested, and then in the sixth, whose caller and seed proofs judged a candidate on the paths that complete alone, so a load standing on a path that ends in a raise was never counted against it, and then in the seventh, whose proofs certified a candidate by the body of one definition alone, never asking whether a call into its name runs that body or whether the module level name still denotes it, so a generator function helper, a helper displaced by a decorator, and a helper rebound by a later module level assignment all carried a certified body no call would run, and then in the eighth, whose rebind reading gathered the assignment family and the definitions and imports standing in a statement but never the binding standing in the head of a compound statement, so a helper rebound by the target of a with item or by a name a match pattern captures still carried its certification, and then in the ninth, whose readings gathered every binding standing in a statement and in the head of one but never entered a definition standing after a candidate, so a helper whose name an installer definition rewrote under a global declaration, run by a module level call standing after it, still carried its certification, and then in the tenth, whose execution reading followed a module level call one body deep and only into the plain definitions standing after a candidate, so a helper whose name an installer rewrote through a second called definition, through a call standing in the body of a class at module level, through the constructor of a class the module level constructed, or from a definition standing before it and called after it still carried its certification, and then in the eleventh, whose execution reading stopped whole at every definition and every lambda it met, reading neither the parts of a definition statement that perform as the statement runs nor the body of a lambda a statement invokes on the spot, and whose construction reading consulted the dunder init method defined in the class itself alone, so a helper whose name an installer rewrote through a call standing in a decorator expression or in a default value of a definition standing after it, or inside a lambda a statement invoked on the spot, or through a dunder new method, or through a constructor inherited from a module level base class still carried its certification, and then in the twelfth, whose call collection read the header of a definition standing in an executing position and the body of a lambda a statement invokes and completed the construction write set of a class, but collected no execution a class statement performs implicitly and returned whole at a class standing inside a body it had entered, so a helper whose name an installer rewrote through the subclass hook of a module level base a subclass named, or through the metaclass a class was defined under, or through a call standing in the decorator expression of a class inside a body the module level called still carried its certification, and then in the thirteenth, whose class creation counting named the subclass hooks of the bases a class names and the construction of the metaclass its keyword names, but not the set name hooks of the descriptors standing in the namespace a class body builds nor the metaclass a class inherits from its bases where it names none, so a helper whose name an installer rewrote through a set name hook on a descriptor a class body carried, or through the construction of a metaclass reached only through a base class, still carried its certification, and then in the fourteenth, whose class creation counting named the subclass hooks of the bases a class names, the set name hooks of the descriptors its body instantiates, and the construction of the metaclass its keyword names or it inherits from its bases, but not the prepare method the metaclass runs before the class body, which resolved a descriptor only where a body assignment instantiated it and never where a plain name placed an instance bound at module level, which read the header of a class standing inside a body it had entered but stopped at that class body, and which resolved every base in bare name form alone, so a helper whose name an installer rewrote through the prepare method of a metaclass named by keyword or inherited through a base, through a set name hook on a descriptor placed in a class body by name, through a plain call standing in a class body inside a function the module level called, or through a subclass hook or an inherited metaclass construction behind a subscripted base still carried its certification, and then in the fifteenth, whose set name counting read only the direct assignments of a class body in direct call form, whose gatherings of the designated methods read a method only where it stood as a definition statement, and whose tables gathered no plain definition standing in a class body, so a helper whose name an installer rewrote through a set name hook on a descriptor placed in an arm of a class body, placed by a tuple assignment, or instantiated through a subscripted class name, through a subclass hook, a set name method, or a construction method bound to its designated name by a plain assignment, or through a plain definition standing in a class body and called from that body still carried its certification, and then in the sixteenth, whose readings turned a name into a class only under the name its definition statement carries, which gathered three of the four designated methods of the instantiation protocol and never the dunder call method that performs the construction, and whose gatherings of the designated methods read only the direct members of a class body, so a helper whose name an installer rewrote through a set name hook on a descriptor instantiated under an aliased class name or reached through an alias bound to a subscript of the class, through a subclass hook behind an aliased base, through the construction of a metaclass named by an aliased keyword value, through a dunder call method of a metaclass named by keyword or inherited through a base, or through a subclass hook defined in an arm of a class body, assigned in an arm, or bound there by a walrus statement still carried its certification, and then in the seventeenth, whose reach resolved the callee of a plain call by the name a definition statement carries alone and never through the module level bindings standing behind the name, which read a decorator expression only for the calls standing inside it and never counted the application of a decorator standing in bare name form, and which resolved an instance name at no call, so a helper whose name an installer rewrote through a call bearing a name bound to the installer itself, to a wrapper that calls the installer, or to a lambda whose body calls the installer, through a decorator standing in bare name form on a definition or a class standing after it, the decorator standing as a definition whose body calls the installer, as a class whose construction calls it, or as an instance whose dunder call method calls it, or through a call naming an instance whose class carries a dunder call method that calls the installer, the instance reached under its own bound name or under a second name bound to the first, still carried its certification, and then in the eighteenth, whose bindings the name resolutions read stood collected from plain and annotated assignments alone while the pair collection beside them gathered walrus bindings and loop targets as bindings of the same standing, which gathered no method under the names the with statement and the for statement perform, which counted no execution for a subscript naming a class, and whose placement reading resolved neither a subscript selecting an element of a tuple nor an instantiation standing in an arm of a conditional expression, so a helper whose name an installer rewrote through a call bearing a name a walrus statement bound to the installer itself or to a lambda whose body calls it, through a subclass hook behind a base a walrus statement bound to the hook's class, through a call naming an instance a walrus statement bound to its instantiation, through a set name hook on a descriptor an instance of which a loop target bound and a class body placed by that name, through the dunder enter or dunder exit method of a manager a with statement entered inline, by a bound name, or through a base, through the dunder class getitem method a subscript performed in a base expression or in an annotation, through the dunder iter or dunder next method of an iterable a for statement walked inline or by a bound name, or through a set name hook on a descriptor selected out of a tuple by a subscript or standing in an arm of a conditional expression, still carried its certification, and then in the nineteenth, whose counting of the dunder next method read the return of the gathered iter method at the one shape of the parameter name standing alone as the returned value, which gathered no dunder getitem method for the sequence fallback a for statement performs where no iter method stands in the chain, which counted no dispatch for an augmented assignment although its own pair collection gathered the statement as a binding form, whose bindings table paired every name an unpacking target binds with the whole of the value it unpacks, whose manager and iterable positions resolved an inline instantiation or a name and no other form, and whose placement reading resolved neither a boolean expression among the values a class body assignment places nor a subscript selecting through a name bound to a literal, so a helper whose name an installer rewrote through a dunder next method behind an iter method returning the instance through a local name bound to its first parameter or through an arm of a conditional expression, through a dunder getitem method of a class carrying no iter method, through a dunder iadd method or the dunder add fallback an augmented assignment performed on a bound instance, through a call bearing a name an unpacking bound to the installer itself, through a subclass hook behind a base an unpacking bound to its class, through an instance call bearing a name an unpacking bound to its instantiation, through the dunder enter method of a manager or the dunder iter method of an iterable standing as a conditional expression or as a walrus binding, or through a set name hook on a descriptor placed by an operand of a boolean expression, at module level or inside a body the module level called, or selected by a subscript through a name bound to a tuple, still carried its certification, and then in the twentieth, whose installation reading consumed the assignment pairs uncorrected and paired a designated name bound by unpacking with the whole of the tuple it unpacks, whose self return reading gathered the bindings inside an iter body through a private collector that flattened unpacking targets against the whole value and followed name valued bindings alone, whose manager and iterable positions resolved a call, a name, a conditional expression, and a walrus expression and no other form, whose subscript counting resolved the value position through a bare name or a subscript alone, whose placement reading followed a placed name only through a call, a further name, or a sequence standing behind it, and whose set name counting handed the placement reading each body pair's value alone so an element an unpacking target binds against a name bound to a literal was never read as an element, so a helper whose name an installer rewrote through a dunder enter or dunder exit method or a dunder iter method a class body installed by unpacking, through a dunder next method behind an iter method returning the instance through a name bound to it by unpacking or through a name bound to a conditional expression whose arms are the parameter, through the dunder enter method of a manager or the dunder iter method of an iterable standing as a boolean expression or as a subscript selecting an element of a literal behind a name, through the dunder class getitem method a subscript performed whose value position stood as a conditional expression, or through a set name hook on a descriptor placed by a name bound to a conditional expression or to a subscript selecting it out of a literal, or placed by an unpacking target against a name bound to a literal, still carried its certification, and then in the twenty-first, whose counting consumed the iterable recording the pair collection makes for a loop target at the call, base, decorator, instance, and manager seats and resolved the name to nothing at all five, whose augmented assignment counting gathered the additive operator's in place and plain pair alone whatever operator the statement carried, whose subscript value reading resolved a conditional expression standing in the value position and neither a boolean expression nor a walrus expression standing there, whose self return scan read no subscript selection and so no return of the instance through an element of a literal, whose self return alias collector paired a plain loop target over a literal with the whole iterable through the bare name branch of the unpacking helper it reused, and whose placement reading followed a placed name through a bound call, a bound name, a bound sequence, a bound conditional, a bound subscript, and a bound boolean expression and not through a bound walrus expression, so a helper whose name an installer rewrote through a call bearing the name of a loop target over the installer itself, through a subclass hook behind a base a loop target bound to its class, through a decorator application or an instance call on a name a loop target bound, through the dunder enter method of a manager a loop target bound, through the dunder isub method, the dunder sub fallback, or the dunder imul method an augmented assignment performed on a bound instance, through the dunder class getitem method a subscript performed whose value position stood as a boolean expression or as a walrus expression, through a dunder next method behind an iter method returning the instance through a subscript selecting it out of a literal, standing in the return or behind a local name bound to the selection, or behind an iter method returning a name a plain loop target over a literal holding the instance bound, or through a set name hook on a descriptor placed by a name bound to a walrus expression yielding its instantiation, still carried its certification, and then in the twenty-second, whose element reading handed every seat the whole of an element an unpacking loop target unpacks and never the component the target pairs its names with, whose subscript value reading fell through to the denotation reading for a subscript standing in the value position and resolved nothing behind the literal the subscript selects from, whose self return reading read a selection only where the literal stood in the node and never behind a name the iter body itself binds, whose element reading answered a chain of loop targets one level short, returning what the middle name denotes where the second loop asks what that name is worth, and whose instance resolution took no subscript value at all, so a helper whose name an installer rewrote through a call naming an instance an unpacking loop target bound to its component, through a subclass hook behind a base an unpacking loop target bound to its class, through the dunder class getitem method of a class a subscript selected out of a literal in its value position, through a dunder next method behind an iter method returning the instance through a selection behind a name its body binds, through a call bearing a name a second loop target bound by walking a first, or through a dunder call method or a dunder isub method of an instance a name bound to a selection out of a literal denotes, still carried its certification, and then in the twenty-third, whose self return selection helper read the value a subscript stands on as a literal node or as a name the body itself binds and read nothing else standing in that position, whose placement, instance, and manager readings routed their selections through a literal reading that resolves a literal standing in the position or a plain name to one and composes no nesting, while the selection reading beside them composed a subscript standing on a subscript at the execution seat alone, and whose literal reading followed no conditional expression, no boolean expression, and no walrus expression standing as the base an inner selection selects out of, so a helper whose name an installer rewrote through a dunder next method behind an iter method returning the instance through a selection whose value stood as a walrus expression, as a boolean expression, as a conditional expression, or as a nested subscript, through a set name hook on a descriptor placed behind a nested selection directly or by a name bound to the selection, through a dunder call method or a dunder isub method of an instance bound to a nested selection, through the dunder enter method of a manager bound to a nested selection, or through the dunder class getitem method of a class an inner selection selected out of a literal an arm of a conditional expression, an operand of a boolean expression, or the value a walrus expression yields held in its own value position, still carried its certification, and then in the twenty-fourth, whose literal sequences reading followed a name to a literal and read a name bound to a subscript as carrying nothing, whose instance resolution followed a call, a name, and a subscript standing in a binding and no yielding bound form although the placement reading followed all three, and whose body's own literal reading in the self return selection followed a body name to a literal node or to a further name and nothing else although the selection helper followed more standing in the subscript itself, so a helper whose name an installer rewrote through a set name hook on a descriptor placed by a selection out of a name bound to a selection, directly or through a second name bound to the selection, through a dunder call method or a dunder isub method of an instance bound to such a selection, through the dunder enter method of a manager bound to such a selection or named by such a selection standing in the position itself, through the dunder class getitem method of a class a selection out of such a name selected, through a dunder call method, a dunder enter method, a dunder isub method, or the dunder call method of a decorator of an instance a name bound to a conditional expression, a boolean expression, or a walrus expression denoted, or through a dunder next method behind an iter method returning the instance through a selection out of a body name bound to a boolean expression, to a conditional expression, to an inner subscript, or by a walrus expression to a boolean expression, or returning a loop target a header over such a name bound, still carried its certification, and then in the twenty-fifth, whose reach queued a call for a bound value of a called name only where the bound value stood as a plain name or as a lambda standing in the binding, following no bound subscript, no bound conditional expression, no bound boolean expression, and no bound walrus expression although the instance, placement, literal, and alias readings beside it followed bound subscripts and bound yielding forms, so a helper whose name an installer rewrote through a call bearing a name bound to a subscript selecting the installer out of a literal, directly or through a further name bound to the literal, through a call bearing a name bound to a conditional expression, a boolean expression, or a walrus expression yielding the installer, through a call bearing a name bound to a subscript selecting a lambda whose body calls the installer out of a literal, or through a decorator standing in bare name form bearing a name bound to a subscript selecting a decorator that calls the installer out of a literal, still carried its certification, and then in the twenty-sixth, whose selection reading resolved a name standing in a subscript's value position through the module level bindings alone, the binding a loop target name carries there being the iterable its header walks, so a subscript standing on such a name selected out of the iterable one level above the element the name denotes, and whose self return selection helper kept only sequence nodes among the elements a selection returned and dropped a selected name where it stood, so a helper whose name an installer rewrote through a call bearing a name bound to a subscript standing on a loop target name over a tuple or a list element alike, through the dunder call method of an instance so bound, through the dunder enter method of a manager standing as such a subscript in the with position or reached through a name bound to it, through the dunder class getitem method a subscript standing on such a selection performed, through the dunder isub method of an instance so bound under an augmented assignment, through a decorator standing in bare name form bearing a name so bound, or through a dunder next method behind an iter method returning the instance through a body local name bound to a subscript selecting a name the body bound to the literal holding the instance, the selection standing in the binding itself, behind a walrus in the return, through a conditional bound name on either arm, or selecting out of a literal a further body local name denoted, still carried its certification, and then in the twenty-seventh, whose selection reading consulted the loop target element reading only where a subscript's value stood as the loop target name itself, so a subscript standing on a conditional expression, a boolean expression, or a walrus expression over such a name, or on a name bound to such a yielding form, selected out of the iterable the name walks rather than out of the element it denotes, and a helper rewritten through a call of a name bound to such a subscript, through the dunder call method of an instance so bound, through the dunder enter method of a manager standing as such a subscript in the with position, through the dunder class getitem method of a class reached through a name bound to such a subscript standing on a walrus expression, through the dunder isub method of an instance so bound under an augmented assignment, through a decorator standing in bare name form bearing a name so bound, or through a call of a name bound to a subscript standing on a name itself bound to a conditional expression or a walrus expression over the loop target name, still carried its certification, and then in the twenty-eighth, whose class getitem name branch took its qualifying subscript only where the name's own binding was the subscript itself and the subscript's base stood in one of its enumerated forms, so a yielding form interposed on either side of the qualifying subscript defeated the resolution, and a helper rewritten through the dunder class getitem method of a class reached through a name bound to a subscript standing on a name bound to a conditional expression, a boolean expression, or a walrus expression over the loop target name, or through a name bound to a conditional expression, a boolean expression, or a walrus expression over the name bound to such a subscript, still carried its certification, and then in the twenty-ninth, whose class getitem name branch followed a yielding form interposed on either side of its qualifying subscript but entered the follow only from a bound value standing as the subscript itself or as a yielding form and qualified a base only where the base name's own bindings held a yielding form, so a plain name hop standing between the resolution and the yielding form that qualifies it defeated the resolution at both loci, and a helper rewritten through the dunder class getitem method of a class reached through a name bound by plain assignment to a name bound to a conditional expression or a boolean expression over the name bound to such a subscript, or through a name bound to a subscript standing on a name bound by plain assignment to a name bound to a conditional expression or a boolean expression over the loop target name, still carried its certification. Every construct the guard touches is modelled in these terms or refused, apart from the one named residual, and shapes the walk cannot resolve to fixture name literals, such as a name arriving as the return of a call into another module, earn no credit and fail the accounting check instead of passing it.
137
+ - **Breaking.** The `PlacesError` constructor no longer copies its three list arguments through a bare `list` call. A list argument whose iteration raised escaped construction as the caller's own exception, and an endless iterable was consumed forever, so the three copies were the one unguarded and unbounded caller iterable read left in the library. A plain list or tuple is already materialised in full, so it is still copied directly, under a guard that surfaces any failure as a chained `ConfigError` and with no ceiling, which keeps the wire details arrays the library itself passes whole at any length. Any other iterable is materialised through the shared bounded read the wire facing surfaces use, so one that raises surfaces as a chained `ConfigError`, and one that runs past the ceiling of 1000 items is refused with a `ConfigError` naming the bound. A construction that handed the constructor an endless or over long generator, or a value whose iteration raises, previously hung, escaped bare, or was accepted, and is now refused inside the exception hierarchy.
138
+ - **Breaking.** An error whose details payload the bindings cannot convert no longer loses its classification. An integer literal past the interpreter limit for exact conversion (4300 digits) inside the details made the conversion of the payload fail, and that failure demoted the whole error to the plainest `PlacesError`, which the Python layer then read as an `ApiError` with no Google status, no error code and no retryable flag. The details surface, the parsed details and the field and quota views taken together, now degrades to empty for that call instead, and the class, Google status, error code and retryable flag stand exactly as the core classified them. A caller who caught the demoted `ApiError` for such a body now catches the classified error with an empty details surface.
139
+ - **Breaking.** Error details are now recovered entry by entry when a body defeats the strict parse. A lone surrogate escape anywhere in the body used to cost the whole details array on the tolerant head scan route, whether it sat inside one entry, where a single string was the whole cause, or in the status or code field outside the details entirely. The scan now reads the array from its own span of the body and parses each entry on its own, so a refused entry degrades alone, clean siblings survive with the field and quota views reading them, a poisoned head field costs the array nothing, and the envelope string cap applies to recovered entries as it does on the whole parse route. The whole array still degrades to empty when the array container itself is unreadable in the text, a cut off array being the standing shape. A body that read with empty details on this route now reads with the recovered entries.
140
+ - **Breaking.** A corrupt gzip error body on the core decode route no longer claims a cap stop that never happened. When the whole compressed body had arrived and its stream failed after yielding output, the failure was read as a capped read and the message reported that the read stopped at the 262144 byte cap. Such a body is now reported unreadable, the same construction the transport route gives the same body under the exact lowercase declaration, while a stream that fails on a genuinely capped raw read keeps the capped reading, because there the failure is the cut. The exception class is unchanged for the anchor shape and for genuinely capped reads. One sub shape does change class, a checksum failed stream whose decompressed partial happened to carry a complete envelope was previously classified by that unverified partial, and such a body now falls back to the bare HTTP classification, the same fallback the transport route always gave it.
141
+ - **Breaking.** `field_violations` no longer fabricates entries for malformed violation values. The view used to emit one dict of two strings per wire entry, replacing any non string member with the empty string, so a malformed entry read exactly like a genuine empty violation and its sent values were lost. A well formed entry, an object whose `field` and `description` members are both strings, still reads as the same dict of the two strings, byte for byte. Every other entry now reads exactly as the wire carried it, a non object entry as itself and an object with a missing or non string member as the whole object, so nothing in the view is invented and a genuine empty violation is distinguishable from a malformed entry again. The view length still equals the number of entries in the wire array, and the `details` attribute beside it is unchanged, as is the `quota_violations` view.
142
+ - A field mask member whose hash, equality or comparison raises can no longer escape standalone mask construction as a bare builtin. The constructor dedupe and the set sort inside `FieldMask.of` read the members once more past the guarded path check, and those reads now sit inside the same typed discipline, so the constructor, `FieldMask.of` and the raw iterable door of `normalize_fields` refuse such a member as a chained `ConfigError`. Well formed members behave exactly as before.
143
+ - A `retry_after` value that the `PlacesError` constructor admits but cannot convert no longer escapes construction bare. An int too large to convert to a float raised `OverflowError`, and a numeric subclass whose conversion raises let its own exception out. A conversion failure now reads as None, on the same terms as a bool or a non numeric value, so the stored value is a float or None as the constructor promises.
144
+ - A panic inside the stored form conversion of an async call can no longer leave the call unsettled. The conversion runs inside the spawned task after the panic guard over the core future returns, so a panic there would have unwound the task before the completion slot was written or the done flag set, and the Python layer would have polled the handle until the caller cancelled. The conversion now runs under the same panic guard discipline as the core future, and a panic settles the call with the same network class error a guarded core panic produces. No input the shipped response types can produce panics in the conversion, whose failure mode is a returned error, so no caller visible behaviour changes.
145
+ - The corpus accounting suite now guards the two blind spots its own machinery carried. The observation linkage is asserted at the same strength as the case linkage, so each observation test must name every fixture its observation claims, its own fixture and its host fixtures alike. Both linkage proofs require the fixture name to reach one of the recorded corpus module loading helper calls in the test, as the literal argument of the call or through a local name bound to the literal, so a fixture name resting in a comment, a docstring, or an assignment no loading call consumes satisfies neither proof. A second guard pins the fixture builder spec list against the coverage manifest recorded set, file for file, with each spec agreeing with its manifest entry on session, exchange and selector place ids. An observation test repointed at another fixture, or a fixture dropped from the builder spec list, now fails the suite instead of silently narrowing the proofs the corpus rests on. No fixture bytes and no library behaviour change.
146
+ - A hostile caller value can no longer escape the exception hierarchy at three more doors. The free text arguments of text search and autocomplete are checked inside the guarded argument phase, client construction reads the API key and the timeout under guard including the reads inside its own refusal messages, and field mask member validation is guarded, so a string subclass whose methods raise now lands as a chained `ConfigError` everywhere instead of a bare `RuntimeError`. A fabricated `FieldMask` whose paths state was replaced after construction is refused by `normalize_fields` as a `ConfigError` before any native call, where a missing paths attribute previously raised `AttributeError` and a non string member escaped as a conversion `TypeError` from the native layer. Ordinary values behave exactly as before.
147
+ - The tolerant envelope head scan now tracks the whole parse on three more body shapes. A later error value that is not an object clears an envelope an earlier error key found, matching the last wins reading of the parse. A malformed head field costs that field only, so a valid status behind a malformed code token and a valid code and message behind a broken status string still classify the error. An envelope behind a sibling container that never closes is recovered by one bounded recovery pass instead of the body falling back to the bare HTTP class. Bodies that defeated the parse in all three shapes previously classified by HTTP status alone.
148
+ - A gzip error body is no longer invisible when its Content-Encoding header names the encoding loosely. The transport decodes only the exact lowercase token, so the case variants and the list forms reached the parser still compressed. The core now decodes those bodies itself, once and under the read cap, on the JSON paths and the photo path alike, so their envelopes classify by Google status. A twice compressed body declared once keeps its previous behaviour, and other encodings are untouched.
149
+ - A process that used the AsyncClient could crash while the interpreter exited, with an abort or a segfault after every result had already been delivered correctly. The cause was the completion machinery of the async bridge the bindings built on, whose per call tasks finished each call from a runtime worker thread and were still entangled with the interpreter while finalization dismantled it. Async calls now run through a call handle owned by the bindings that completes into Rust state only, and the Python layer collects each result from its own event loop thread. No public API or result changes. The one cost is completion latency, because a result is delivered on the next pump poll after it lands, sub-millisecond in the common case and at most one settled 4 ms poll interval plus loop scheduling in the worst case. Completion polling and result conversion also run on the caller's event loop thread, so an async call spends more CPU on the calling thread than the retired bridge design did, measured at this head as an increase of 68 to 219 percent in calling thread CPU against the pre repair baseline, varying by method, while still spending less than the synchronous client on every method. Standing subprocess tests pin the exit shapes that used to crash.
150
+ - A Tokio runtime failure can no longer escape as a panic outside the exception hierarchy. When the operating system refuses the runtime build or its worker launch (thread or memory exhaustion), the first affected call now raises `NetworkError` naming the refusal or the panic payload, on both clients, instead of a `pyo3_runtime.PanicException` with no result. Panics raised while the runtime drives a call are converted the same way.
151
+ - A synthesized test fixture and its pin were corrected to the documented shape. The synthesized consumer alert fixture carried `consumerAlert.details` as a list of one Details object, where both the REST JSON representation and the RPC reference document a single Details object, and the test that pins the fixture indexed it as a list. The fixture now carries the single object and the pin reads it as one. No library behaviour changes.
152
+ - Error envelopes that arrive on a success HTTP status now raise the typed exception for their Google status on every endpoint, photos included, instead of decoding as empty results or returning the envelope JSON as image bytes.
153
+ - Response bodies are read under a size cap, gzipped bodies decompress, and a failed body read keeps both the HTTP status and the read failure in the resulting error instead of dropping one of them.
154
+ - Photo downloads are checked against their declared content type and the signature of the format it claims before the bytes can be saved as an image, and photo failures served by an image host after a redirect are worded as image host failures.
155
+ - Place ids and photo resource names are validated strictly and percent encoded segment by segment, closing query injection, fragment truncation and path traversal through both values.
156
+ - Non-finite timeout values raise `ConfigError` instead of panicking across the FFI boundary, timeouts are bounded at one hour, and numeric arguments such as result counts and photo dimensions are range validated in Python before they reach the core.
157
+ - Envelope error messages are capped at 500 characters, `google_status` is `None` rather than an empty string when no envelope status was parsed, and server side canonical statuses such as `DEADLINE_EXCEEDED` classify as `ApiError` instead of falling through to the HTTP classes.
158
+ - The envelope status text now joins the 500 character cap the envelope message and the detail strings already honour. It previously passed through whole into `google_status` and the rendered exception, bounded only by the response body caps, so a hostile or broken endpoint could park an arbitrarily long status inside an exception callers routinely log and retain. Classification is unchanged, because a status longer than the cap never matched a canonical status and still falls through to the HTTP class.
159
+ - Exceptions now carry `status_mismatch`, `retryable`, `retry_after` and `context` attributes, model conversion failures raise `DecodeError` instead of escaping as bare builtin exceptions, and unknown top level fields on Autocomplete responses survive into `raw` as they already did on search.
160
+ - `page_size` gets the same range validation as the other numeric arguments instead of reaching the API unchecked or leaking an internal conversion error, error envelopes carrying a code or a message but no status string raise the exception their numeric code maps to instead of succeeding silently, and error envelopes on the photo download path raise that mapped exception with its Google status and server message instead of a decode error that dropped both.
161
+ - An empty nested response object decodes as absence instead of failing the entire response, so an empty location object reads as no location, exactly as a null location does. A partial object carrying one member of a required pair no longer decodes with a fabricated default for the other member, and refuses as the coordinate fabrication entry in the Changed section records.
162
+ - Unknown fields inside nested response objects are preserved into the raw view like top level fields, counts and dimensions that arrive string encoded or as exactly integral floats decode normally, a zero start offset in autocomplete matches survives reserialization, and unknown fields carrying integers beyond 64 bits keep every digit.
163
+ - One mistyped field in an error envelope no longer discards the Google status, message and details the rest of the envelope carried, and retryability follows the HTTP status on every error type that carries one instead of only one variant.
164
+ - A failed photo request can no longer leak the API key into the exception message through the photo URL, a photo response with no declared image content type is refused instead of being returned as photo bytes, an empty JSON object in the skip redirect photo form is a decode error instead of a photo result pointing at an empty URI, and a failed body read after a success status carries that status on the error instead of losing it.
165
+ - Empty optional strings and lists are omitted from request bodies and empty Place Details query parameters are omitted, instead of being sent as empty values whose meaning differs from absence at the API, and a zero response size cap is rejected when the client is built instead of failing every call.
166
+ - A request body whose circle omits its center, or whose center omits a latitude or a longitude, fails locally with a configuration error instead of being sent aimed at latitude 0.0 and longitude 0.0. The lenient defaults response decoding once applied to a partial coordinate survive only in the core internal typed reads, and no result a Python caller can observe carries them.
167
+ - A bare string passed for an iterable of strings parameter (`fields`, `price_levels`, the type and region filters) is rejected with `ConfigError` before any request, instead of being shredded into one-character items and sent. Field masks validate the wire grammar locally, with camelCase segments, no empty segments, whitespace, commas or wildcards, duplicates removed, and set input sorted for a deterministic header.
168
+ - Boolean request fields accept only real bools, in the Python body builders and in the Rust conversion layer, so the string "false" can no longer reach the wire as true through any path, including dict shaped routing parameters.
169
+ - Coordinates are validated in Python in every slot (nearby location, origins, circle centers, rectangle corners, wire shape dicts). Sets are rejected because their order is arbitrary, bools and strings are not numbers, and NaN, infinity and out of range values raise `ConfigError` before the request is built.
170
+ - Local misuse that used to escape as bare builtin exceptions (a non-string API key, place id or query, a list where a location dict belongs, a non-finite radius) now raises `ConfigError` naming the parameter, locally raised errors carry the calling method as their context, and `place_photo` enforces its at-least-one-dimension rule before the native call.
171
+ - Calling the sync `Client` from inside a running event loop raises `ConfigError` pointing at `AsyncClient` instead of freezing the loop for the request duration.
172
+ - Pickling or copying a client raises a `TypeError` naming placekit and the real reason, instead of an error naming a phantom `builtins.Client` class, and shallow copy no longer silently shares the live native client.
173
+ - The shipped type stubs match the runtime, with constructors for `Place` and `PlacePrediction`, optional `fields` on the native methods, `retry_after` as a float, the frozen `FieldMask` dataclass, and `raise_translated` declared with its `NoReturn` type.
174
+ - The source distribution ships the repo toolchain pin file, so a build from the sdist under rustup selects the pinned 1.88.0 toolchain rather than the machine default. The pin carries a minimal profile with the rustfmt and clippy components beside the compiler, so a rustup that cannot supply those components cannot sync the pin, and where rustup is not installed the file has no effect and the build uses the toolchain already present, bounded by the declared Rust floor.
175
+ - `PhotoUriResult` now carries the full JSON form the photo media endpoint returned on its `raw`, deep copied like the other result raws. The binding previously crossed only the name and the URI, so any field Google adds to that form was dropped outright instead of surviving unmodelled.
176
+ - Country routing inside `placekit.address` now matches without regard to letter case, so a place whose country code arrives in a lower case form reaches the same stated answers as the upper case form, while the country value the helpers return stays exactly as sent.
177
+ - The Rust core's `Client` and `ClientBuilder` no longer print the API key under Debug formatting. Both types implement Debug by hand and show a redaction marker in place of the key, so logging a freshly built client cannot write the credential into application logs.
178
+ - `PhotoUriResult` no longer prints its signed photo URI in its repr. The URI is a bearer capability that fetches the photo without the API key until it expires, and the field is now excluded from repr the way the bytes model already excluded its content.
179
+ - An unknown response field carrying an integer past the interpreter conversion limit now raises `DecodeError` instead of a bare builtin `ValueError` outside the exception hierarchy, on both clients.
180
+ - The photo redirect screening now judges IPv4-mapped and IPv4-compatible IPv6 spellings, the whole 0.0.0.0/8 range and the 100.64.0.0/10 shared address space by the address they reach, so those spellings of a loopback, private or special purpose target are refused like the plain IPv4 spelling instead of passing the screen.
181
+ - A call that began while its client was open and then raced a `close()` from another thread could fail with a bare `AttributeError` outside the exception hierarchy, deterministically when a slow caller iterable held the call in body building while the close landed. Every call now captures the native handle at entry and uses the captured handle throughout, so a racing call completes with its own result on all five methods of both clients, and only calls that start after the close raise `ConfigError`.
182
+ - `retry_after` now honours the float type its stubs always declared. An integer header value arrives as a float, a fractional header value keeps its fraction instead of being dropped to None, a negative value reads as no value, and the HTTP date form yields the seconds until that date as a float, with a past date still meaning retry now at 0.0.
183
+ - Standalone `FieldMask` construction can no longer escape the exception hierarchy. A member whose repr raises used to surface as that bare `RuntimeError` out of both the constructor and `FieldMask.of`, because the error message formatted the member before the error existed, and a set or frozenset mixing strings with other types used to escape as a bare `TypeError` from the documented sort. Both now raise `ConfigError` with the member rendered safely, matching the client path, and `FieldMask.of` accepts an existing `FieldMask` and returns an equal mask, as the client methods already accepted one in the `fields` argument.
184
+ - A locally raised `ConfigError` that a client method annotates with its context no longer renders as a duplicate pair. The method used to rebuild the error and chain the rebuild to the original, so one failure printed as two exceptions of the same type carrying the same message. The annotation now happens on the original exception, and a cause the original already carried, such as a hostile iterable's own failure, survives in the chain.
185
+ - A decode failure inside a list of child models now names the position that failed. The message carries the parent model, the wire key and the element index in front of the child's own message, and a decode failure at the response root no longer renders the placeholder path the path tracker reports for a segment it cannot name.
186
+ - The star import surface is declared. `placekit.models`, `placekit.fields`, `placekit.exceptions` and `placekit.client` carry `__all__` matching their type stubs exactly, so a star import no longer binds dozens of names the modules never meant to export, and the stubs declare the client pickle and copy refusals, the `PlacesError` string forms and the exit signatures in the shape the runtime actually has.
187
+ - A process that called placekit and then forked left every later call in the child hanging forever, on fresh clients as much as inherited ones, because the process global Tokio runtime's worker threads do not survive a fork. The bindings now keep their own fork aware runtime, keyed by the process id it was built under, and rebuild both the runtime and each client's connection state on the first call a forked child makes, so calls in the child work.
188
+ - The photo content type gate in the Rust core judged the declared type case sensitively while the signature check behind it lowercases, so a legal uppercase media type such as `IMAGE/PNG` carrying a genuine image was refused. The gate now normalises case before judging, matching the signature check, and a refused type is still refused in any case spelling.
189
+ - An integer past the interpreter string conversion limit stored in a typed integer slot no longer breaks the result it lands on. It used to decode successfully and then raise a bare `ValueError` the first time the result was printed or logged, and it now refuses at decode time with a `DecodeError`, matching what the client path already did with the same value at the bindings.
190
+ - A response body carrying a very large scalar value ahead of a value nested past the parser depth limit no longer stalls the call that reads it. The whole document parse attempts behind error classification and response decoding priced such a body at roughly the open frame count times the bytes before the failure point, seconds on a multi megabyte body, and an error envelope on a success status paid the attempt twice. A body past the error read cap size whose nesting reaches the depth limit is now recognised structurally before the parse runs, at the tolerant scan's per byte rate, and refused or classified from the scan with the same exception, status and message as the parse produced, while the success status envelope check parses the body once instead of twice. Bodies at or below that size are untouched and keep the parse's own word for every failure.
191
+ - A process forked while a client conversion held its decode ownership window open no longer retains the parent process's parsed response tree for the child's whole life. The ownership accessor refused the inherited window on the process check but left the state in place, and the state holds the tree. The refusal now clears the state, so the child's first decode releases the tree, while the parent's window and the child's own conversions are unaffected.
192
+ - The `PhotoMedia.save` leftover behaviour is now stated in the save docstring and in the README errors passage. The write is a single non-atomic write, so a save refused part way leaves what was written at the target, a zero byte file when the write is refused before any bytes land and the written prefix when it is refused part way through, and an existing file is truncated before the write begins, so a refused save can leave the target shorter than the file it replaced. The behaviour itself is unchanged, and filesystem failures still raise the `OSError` family by design.
193
+ - The source distribution now carries the cargo audit disposition record at `.cargo/audit.toml` beside the sources it dispositions. The build tool's standing dot path rule had left the file out, so an audit run from the source distribution resurfaced two published advisories without the dispositions and the reachability rationale the repository records for them, while the repository itself carried the posture all along. A caller installing from the wheel is unaffected. A test now pins the carriage on the parsed include entry in the build manifest, so dropping the entry or retargeting its path or format fails the suite instead of regressing the carriage silently. The pin now parses the manifest with a real TOML parser on every interpreter, tomllib where the interpreter carries it and its tomli backport on the floor interpreter, and reads the audit record's dispositions as parsed data, so an emptied record fails the pin as well. The wheels workflow's full suite leg now installs the pinned maturin as well, so that leg runs the artifact guard with the build tool it needs instead of failing for an environment reason.
194
+ - Release wheels now carry the full locked dependency tree in their embedded SBOM, as the README describes. A wheel built for one platform used to carry only that platform branch of the tree, because the build tool generates the document from the target filtered graph, so the first release run's own SBOM guard refused every wheel and nothing published. The release workflow now generates one full tree document with the same generator at the same version the build tool itself vendors, and embeds that document into every wheel before upload. The guard and the README promise stand exactly as written.