Examples ======== The examples below use ``PyMetKit``'s public API and are executed as sybil tests. The building and accessing examples are self-contained; the expanding, equality, merging and parsing examples require the MARS language definitions shipped with ``metkit`` (see :doc:`installation`). Building a request ------------------ A :class:`~pymetkit.MarsRequest` is a verb plus a selection of parameters passed as a plain mapping. Values may be scalars, numbers, collections, or ``/``-separated strings. .. code-block:: python from pymetkit import MarsRequest request = MarsRequest("retrieve", {"class": "od", "param": [151, 129]}) assert request.verb() == "retrieve" assert request["class"] == "od" assert request["param"] == ['151', '129'] Numbers, ranges and ``/``-separated strings are normalized to lists of strings: .. code-block:: python request = MarsRequest("retrieve", {"step": range(0, 13, 6), "date": "20200101/20200102"}) assert request["step"] == ["0", "6", "12"] assert request["date"] == ["20200101", "20200102"] Accessing values ---------------- A request behaves like a read/write mapping. A parameter with a single value returns a scalar, one with several values returns a list: .. code-block:: python request = MarsRequest("retrieve", {"class": "od", "param": [151, 129]}) assert "class" in request assert sorted(request.keys()) == ["class", "param"] assert request.num_values("param") == 2 request["expver"] = "0001" assert request["expver"] == "0001" Iterating over a request yields ``(name, value)`` pairs, mirroring a plain dict: .. code-block:: python request = MarsRequest("retrieve", {"class": "od", "param": [151, 129]}) assert list(request) == [("class", "od"), ("param", ["151", "129"])] Pass a request to :func:`dict` to get a plain Python mapping, useful for serialisation or inspection: .. code-block:: python request = MarsRequest("retrieve", {"class": "od", "param": [151, 129]}) assert dict(request) == {"class": "od", "param": ["151", "129"]} Manipulating a request ---------------------- Parameters can be added or overwritten after construction. All value forms accepted at construction time are also valid for assignment — scalars, integers, ranges, lists and ``/``-separated strings are all normalised the same way: .. code-block:: python from pymetkit import MarsRequest request = MarsRequest("retrieve", {"class": "od", "expver": "0001"}) request["date"] = "20230101/20230102" # slash-separated → list request["step"] = range(0, 13, 6) # range → list request["param"] = 130 # integer → scalar assert request["date"] == ["20230101", "20230102"] assert request["step"] == ["0", "6", "12"] assert request["param"] == "130" request["step"] = [0, 6] # overwrite with a shorter list assert request["step"] == ["0", "6"] A new request can be derived from an existing one by snapshotting it with :func:`dict`, adjusting selected values, and constructing a fresh request: .. code-block:: python from pymetkit import MarsRequest base = MarsRequest("retrieve", {"class": "od", "date": "-1", "param": "130", "step": "0"}) derived = MarsRequest(base.verb(), {**dict(base), "date": "20230101"}) assert derived["date"] == "20230101" assert derived["param"] == "130" Use ``in`` to guard access to parameters that may not be present: .. code-block:: python from pymetkit import MarsRequest request = MarsRequest("retrieve", {"class": "od", "param": "130"}) step = request["step"] if "step" in request else "0" assert step == "0" Equality -------- Two requests are equal when they expand to the same result. MARS language aliases are resolved, so ``"od"`` and ``"operations"`` compare equal. Equality requires the MARS language definitions to be present. .. code-block:: python from pymetkit import MarsRequest r1 = MarsRequest("retrieve", {"class": "od", "date": "20230101", "param": "130"}) r2 = MarsRequest("retrieve", {"class": "operations", "date": "20230101", "param": "130"}) r3 = MarsRequest("retrieve", {"class": "od", "date": "20230101", "param": "131"}) assert r1 == r2 # "od" and "operations" are aliases assert r1 != r3 Expanding and validating ------------------------ :func:`~pymetkit.expand` returns one or more requests expanded against the MARS language definition. :meth:`~pymetkit.MarsRequest.validate` checks a request without inheriting defaults and raises :class:`~pymetkit.MetKitException` on invalid input. .. code-block:: python from pymetkit import MarsRequest, expand request = MarsRequest( "retrieve", { "class": "od", "domain": "g", "date": "-1", "expver": "0001", "step": range(0, 13, 6), }, ) expanded = expand(request) # inherit=True: fills in default values assert expanded.verb() == "retrieve" request.validate() # raises MetKitException if invalid When expanding multiple requests, pass them as a list. Internal language checks are performed once for the whole batch rather than once per request: .. code-block:: python from pymetkit import MarsRequest, expand requests = [ MarsRequest("retrieve", {"class": "od", "date": "-1", "param": str(p)}) for p in [129, 130, 131] ] expanded = expand(requests) assert len(expanded) == 3 Merging requests ---------------- :meth:`~pymetkit.MarsRequest.merge` combines the values of two requests that carry the same parameters, keeping ``self``'s values first and appending only the values of ``other`` that are not already present: .. code-block:: python from pymetkit import MarsRequest left = MarsRequest("retrieve", {"class": "od", "date": "-1", "levtype": "sfc"}) right = MarsRequest("retrieve", {"class": "od", "date": "20230101", "levtype": "sfc"}) merged = left.merge(right) assert merged["date"] == ["-1", "20230101"] Splitting requests ------------------ :meth:`~pymetkit.MarsRequest.split` decomposes a request into one request per value combination across the listed keys. All other parameters are carried over unchanged. The operation is purely structural and does not require the MARS language definitions. .. code-block:: python from pymetkit import MarsRequest request = MarsRequest( "retrieve", {"class": "od", "step": range(0, 13, 6), "param": [129, 130]}, ) parts = request.split(["step", "param"]) assert len(parts) == 6 # 3 steps × 2 params assert parts[0]["step"] == "0" assert parts[0]["param"] == "129" Parsing requests ---------------- :func:`~pymetkit.parse_mars_request` parses one or more requests from a string or a file-like object: .. code-block:: python from pymetkit import parse_mars_request requests = parse_mars_request("retrieve,class=od,date=-1,param=129,step=12") assert len(requests) == 1 assert requests[0].verb() == "retrieve"