Skip to content

Commit 63a8ae0

Browse files
committed
Match canonical ISO fields and reviewed Core/Deep responses
Align Country, State and Language wire mappings, types, examples and documentation with the approved current API cleanup. Preserve existing endpoint methods, frozen wire adapters, authoritative nulls and unrelated products. Currency restoration remains in its separate release batch. Version 1.10.6 is the exact patch approved by Billy. Package interface and runtime transport remain compatible; metadata/docs match the live response.
1 parent 152e1cf commit 63a8ae0

7 files changed

Lines changed: 147 additions & 5 deletions

File tree

‎README.md‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ Discovery example: `parse.company.search(country="US", industry="0700", industry
7777
Supply `industry` and `industry_type` together. The supported namespace is `sic`, with an exact four-digit string such as `0700`; leading zeros are meaningful. Country-only discovery is also supported. Filters intersect and may narrow an existing selector. Country matches the profile country, not a headquarters or operating-presence claim. Unknown values do not match a requested filter. Filter-only candidates use `match.field: "filters"` and `match.value: null`; reuse the same filters and limit with a returned cursor. Counts describe this directory edition, not complete country coverage.
7878

7979

80+
## ISO identifiers
81+
82+
Country uses `iso_alpha3` and `iso_numeric`, State uses the full
83+
`iso_subdivision` code such as `US-CA`, Language uses `iso_alpha3`, and Currency
84+
uses `iso_numeric`. Primary `country`, `state`, `language` and `currency` codes
85+
keep their meaning. Results retain the selected API contract's exact keys,
86+
values, nulls and nesting. Earlier server contracts retain their earlier ISO
87+
spellings. Use the existing lookup methods for each resource.
88+
8089
## Calls
8190

8291
One method per endpoint, named after the route.
@@ -225,7 +234,7 @@ Source deep adds `standard_offset`, `standard_offset_seconds`, signed `dst_offse
225234

226235
## Place statistics and optional detail
227236

228-
Country Core contains the everyday reference: `name`, `name_full`, `name_local`, demonym and country codes, continent, region and subregion, currency, calling code, `flag`, language and timezone codes, borders, bloc memberships and `capital: { name, lat, lon }`. Paid Deep adds specialist detail, including population, total area and national statistics. `gdp_usd` and `gdp_per_capita_usd` are current US dollars; `population_growth_percent`, `urban_population_percent` and `internet_users_percent` use a 0–100 percent scale (growth can be negative or exceed 100). GDP per person and these three percentages round once to two decimals in the API response. `elevation_average` is average terrain elevation in meters; `lowest_point.elevation` and `highest_point.elevation` keep their point-specific meaning. Unknown stays null and a known zero stays zero. Country tax references are grouped once as `deep.tax: { name, rate }`; the rate remains a percentage. Read capital from the Core object and read the tax name and rate from the Deep tax object.
237+
Country Core contains the everyday reference: `name`, `name_full`, `name_local`, demonym, `iso_alpha3` and `iso_numeric` country identifiers, continent, region and subregion, currency, calling code, `flag`, language and timezone codes, borders, bloc memberships and `capital: { name, lat, lon }`. Paid Deep adds specialist detail, including population, total area and national statistics. `gdp_usd` and `gdp_per_capita_usd` are current US dollars; `population_growth_percent`, `urban_population_percent` and `internet_users_percent` use a 0–100 percent scale (growth can be negative or exceed 100). GDP per person and these three percentages round once to two decimals in the API response. `elevation_average` is average terrain elevation in meters; `lowest_point.elevation` and `highest_point.elevation` keep their point-specific meaning. Unknown stays null and a known zero stays zero. Country tax references are grouped once as `deep.tax: { name, rate }`; the rate remains a percentage. Read capital from the Core object and read the tax name and rate from the Deep tax object.
229238

230239
Australian postal lookups include core `localities` with suburb choices (`city`, `state`, `state_name`) on every plan. Null or an omitted field means unknown, while `[]` means the reviewed reference has no eligible choices. `city` stays null when the source is ambiguous, even if there is only one eligible choice. Let the user select their suburb and keep manual entry available. These are geographic choices, not mailing-address verification. [G-NAF source, adaptations and licence](https://parseapi.com/legal/attribution#postal-au).
231240

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "parseapi"
7-
version = "1.10.5"
7+
version = "1.10.6"
88
description = "Official ParseAPI client for Python. One key, minimal JSON, fast."
99
readme = "README.md"
1010
license = "MIT"

‎smoke/smoke.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def expect_error(name, call, code):
5151
expect_ok("ip.self", lambda: parse.ip.self(), lambda r: None if r.get("ip") else "no ip")
5252
expect_ok("continent", lambda: parse.continent("NA"), lambda r: None if r["name"] == "North America" else "wrong name")
5353
expect_ok("continent.countries", lambda: parse.continent.countries("NA"), lambda r: None if r["countries"] else "empty")
54-
expect_ok("country", lambda: parse.country("US"), lambda r: None if r["iso3"] == "USA" else "wrong iso3")
54+
expect_ok("country", lambda: parse.country("US"), lambda r: None if r.get("iso_alpha3", r.get("iso3")) == "USA" else "wrong country alpha-3 code")
5555
expect_ok("country.states", lambda: parse.country.states("US"), lambda r: None if len(r["states"]) >= 50 else "too few")
5656
expect_ok("state", lambda: parse.state("NC", country="US"), lambda r: None if r["name"] == "North Carolina" else "wrong")
5757
expect_ok("state.districts", lambda: parse.state.districts("NC", country="US"), lambda r: None if r["districts"] else "empty")

‎src/parseapi/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
from .city import CityAmbiguityCandidate
44
from ._client import AsyncParseAPI, ParseAPI, ParseAPIError
55

6-
__version__ = "1.10.5"
6+
__version__ = "1.10.6"
77
__all__ = ["CityAmbiguityCandidate", "ParseAPI", "AsyncParseAPI", "ParseAPIError", "__version__"]

‎src/parseapi/_client.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import httpx
1414
from .city import CityAmbiguityCandidate, city_error_candidates
1515

16-
VERSION = "1.10.5"
16+
VERSION = "1.10.6"
1717
_API_VERSION = "2.0.0"
1818
DEFAULT_BASE_URL = "https://api.parseapi.com"
1919
DEFAULT_TIMEOUT = 10.0

‎tests/fixtures/iso-contract.json‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
[
2+
{
3+
"operation": "country",
4+
"code": "US",
5+
"deep": false,
6+
"body": {
7+
"country": "US",
8+
"iso3": "USA",
9+
"numeric": 840
10+
}
11+
},
12+
{
13+
"operation": "country",
14+
"code": "US",
15+
"deep": false,
16+
"body": {
17+
"country": "US",
18+
"iso_alpha3": "USA",
19+
"iso_numeric": 840
20+
}
21+
},
22+
{
23+
"operation": "country",
24+
"code": "AQ",
25+
"deep": false,
26+
"body": {
27+
"country": "AQ",
28+
"iso_alpha3": "ATA",
29+
"iso_numeric": 10
30+
}
31+
},
32+
{
33+
"operation": "state",
34+
"code": "CA",
35+
"country": "US",
36+
"deep": true,
37+
"body": {
38+
"state": "CA",
39+
"country": "US",
40+
"deep": {
41+
"iso_3166_2": "US-CA"
42+
}
43+
}
44+
},
45+
{
46+
"operation": "state",
47+
"code": "CA",
48+
"country": "US",
49+
"deep": false,
50+
"body": {
51+
"state": "CA",
52+
"country": "US",
53+
"iso_subdivision": "US-CA"
54+
}
55+
},
56+
{
57+
"operation": "language",
58+
"code": "hr",
59+
"deep": true,
60+
"body": {
61+
"language": "hr",
62+
"deep": {
63+
"iso3": "hrv",
64+
"countries": [
65+
"BA",
66+
"HR"
67+
]
68+
}
69+
}
70+
},
71+
{
72+
"operation": "language",
73+
"code": "hr",
74+
"deep": false,
75+
"body": {
76+
"language": "hr",
77+
"iso_alpha3": "hrv",
78+
"countries": [
79+
"BA",
80+
"HR"
81+
]
82+
}
83+
},
84+
{
85+
"operation": "language",
86+
"code": "gsw",
87+
"deep": false,
88+
"body": {
89+
"language": "gsw",
90+
"iso_alpha3": "gsw"
91+
}
92+
}
93+
]

‎tests/test_iso_contract.py‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import asyncio
2+
import json
3+
from pathlib import Path
4+
5+
import httpx
6+
from parseapi import ParseAPI, AsyncParseAPI
7+
8+
CASES = json.loads((Path(__file__).parent / "fixtures/iso-contract.json").read_text())
9+
10+
def options(sample):
11+
return {"deep": sample["deep"], **({"country": sample["country"]} if "country" in sample else {})}
12+
13+
def test_iso_contracts_remain_raw_through_existing_sync_transport():
14+
for sample in CASES:
15+
calls = []
16+
def transport(request):
17+
calls.append(request)
18+
return httpx.Response(200, json=sample["body"])
19+
with ParseAPI("fixture", transport=httpx.MockTransport(transport)) as client:
20+
operation = getattr(client, sample["operation"])
21+
result = operation(sample["code"], **options(sample))
22+
assert result == sample["body"]
23+
assert len(calls) == 1
24+
assert calls[0].url.path == f"/{sample['operation']}/{sample['code']}"
25+
assert calls[0].url.params.get("deep") == ("true" if sample["deep"] else None)
26+
27+
def test_iso_contracts_remain_raw_through_existing_async_transport():
28+
async def run():
29+
for sample in CASES:
30+
calls = []
31+
def transport(request):
32+
calls.append(request)
33+
return httpx.Response(200, json=sample["body"])
34+
async with AsyncParseAPI("fixture", transport=httpx.MockTransport(transport)) as client:
35+
operation = getattr(client, sample["operation"])
36+
result = await operation(sample["code"], **options(sample))
37+
assert result == sample["body"]
38+
assert len(calls) == 1
39+
asyncio.run(run())
40+

0 commit comments

Comments
 (0)