Source code for phantom.iso3166

"""
Exposes a :py:class:`CountryCode` type that is a union of a :py:class:`Literal`
containing all ISO3166 alpha-2 codes, and a phantom type that parses alpha-2 codes at
runtime. This allows mixing statically known values with runtime-parsed values, like
so:

.. code-block:: python

    countries: tuple[CountryCode] = ("SE", "DK", ParsedAlpha2.parse("FR"))
"""
from __future__ import annotations

from typing import Final
from typing import Literal
from typing import Union
from typing import cast

from typing_extensions import get_args

from phantom import Phantom
from phantom import _hypothesis
from phantom.bounds import parse_str
from phantom.predicates.collection import contained
from phantom.schema import Schema

__all__ = (
    "LiteralAlpha2",
    "ParsedAlpha2",
    "Alpha2",
    "CountryCode",
    "is_alpha2_country_code",
    "normalize_alpha2_country_code",
    "InvalidCountryCode",
)

LiteralAlpha2 = Literal[
    "AF",
    "AX",
    "AL",
    "DZ",
    "AS",
    "AD",
    "AO",
    "AI",
    "AQ",
    "AG",
    "AR",
    "AM",
    "AW",
    "AU",
    "AT",
    "AZ",
    "BS",
    "BH",
    "BD",
    "BB",
    "BY",
    "BE",
    "BZ",
    "BJ",
    "BM",
    "BT",
    "BO",
    "BQ",
    "BA",
    "BW",
    "BV",
    "BR",
    "IO",
    "BN",
    "BG",
    "BF",
    "BI",
    "KH",
    "CM",
    "CA",
    "CV",
    "KY",
    "CF",
    "TD",
    "CL",
    "CN",
    "CX",
    "CC",
    "CO",
    "KM",
    "CG",
    "CD",
    "CK",
    "CR",
    "CI",
    "HR",
    "CU",
    "CW",
    "CY",
    "CZ",
    "DK",
    "DJ",
    "DM",
    "DO",
    "EC",
    "EG",
    "SV",
    "GQ",
    "ER",
    "EE",
    "ET",
    "FK",
    "FO",
    "FJ",
    "FI",
    "FR",
    "GF",
    "PF",
    "TF",
    "GA",
    "GM",
    "GE",
    "DE",
    "GH",
    "GI",
    "GR",
    "GL",
    "GD",
    "GP",
    "GU",
    "GT",
    "GG",
    "GN",
    "GW",
    "GY",
    "HT",
    "HM",
    "VA",
    "HN",
    "HK",
    "HU",
    "IS",
    "IN",
    "ID",
    "IR",
    "IQ",
    "IE",
    "IM",
    "IL",
    "IT",
    "JM",
    "JP",
    "JE",
    "JO",
    "KZ",
    "KE",
    "KI",
    "KP",
    "KR",
    "XK",
    "KW",
    "KG",
    "LA",
    "LV",
    "LB",
    "LS",
    "LR",
    "LY",
    "LI",
    "LT",
    "LU",
    "MO",
    "MK",
    "MG",
    "MW",
    "MY",
    "MV",
    "ML",
    "MT",
    "MH",
    "MQ",
    "MR",
    "MU",
    "YT",
    "MX",
    "FM",
    "MD",
    "MC",
    "MN",
    "ME",
    "MS",
    "MA",
    "MZ",
    "MM",
    "NA",
    "NR",
    "NP",
    "NL",
    "NC",
    "NZ",
    "NI",
    "NE",
    "NG",
    "NU",
    "NF",
    "MP",
    "NO",
    "OM",
    "PK",
    "PW",
    "PS",
    "PA",
    "PG",
    "PY",
    "PE",
    "PH",
    "PN",
    "PL",
    "PT",
    "PR",
    "QA",
    "RE",
    "RO",
    "RU",
    "RW",
    "BL",
    "SH",
    "KN",
    "LC",
    "MF",
    "PM",
    "VC",
    "WS",
    "SM",
    "ST",
    "SA",
    "SN",
    "RS",
    "SC",
    "SL",
    "SG",
    "SX",
    "SK",
    "SI",
    "SB",
    "SO",
    "ZA",
    "GS",
    "SS",
    "ES",
    "LK",
    "SD",
    "SR",
    "SJ",
    "SZ",
    "SE",
    "CH",
    "SY",
    "TW",
    "TJ",
    "TZ",
    "TH",
    "TL",
    "TG",
    "TK",
    "TO",
    "TT",
    "TN",
    "TR",
    "TM",
    "TC",
    "TV",
    "UG",
    "UA",
    "AE",
    "GB",
    "US",
    "UM",
    "UY",
    "UZ",
    "VU",
    "VE",
    "VN",
    "VG",
    "VI",
    "WF",
    "EH",
    "YE",
    "ZM",
    "ZW",
]
"""Literal of all ISO3166 alpha-2 codes. """

ALPHA2: Final = frozenset(get_args(LiteralAlpha2))
is_alpha2_country_code = contained(ALPHA2)


[docs]class InvalidCountryCode(TypeError): ...
[docs]def normalize_alpha2_country_code(country_code: str) -> ParsedAlpha2: """ Normalize mixed case country code. :raises InvalidCountryCode: """ normalized = country_code.upper() if not is_alpha2_country_code(normalized): raise InvalidCountryCode return cast(ParsedAlpha2, normalized)
[docs]class ParsedAlpha2(str, Phantom, predicate=is_alpha2_country_code):
[docs] @classmethod def parse(cls, instance: object) -> ParsedAlpha2: """ Normalize mixed case country code. :raises InvalidCountryCode: """ return normalize_alpha2_country_code(parse_str(instance))
@classmethod def __schema__(cls) -> Schema: return { **super().__schema__(), # type: ignore[misc] "description": "ISO3166-1 alpha-2 country code", "examples": ["NR", "KZ", "ET", "VC", "AE", "NZ", "SX", "XK", "AX"], "format": "iso3166-1 alpha-2", "title": "Alpha2", } @classmethod def __register_strategy__(cls) -> _hypothesis.SearchStrategy | None: from hypothesis.strategies import sampled_from return sampled_from(sorted(ALPHA2))
Alpha2 = Union[LiteralAlpha2, ParsedAlpha2] CountryCode = Alpha2