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 Installation).

Building a request#

A MarsRequest is a verb plus a selection of parameters passed as a plain mapping. Values may be scalars, numbers, collections, or /-separated strings.

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:

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:

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:

request = MarsRequest("retrieve", {"class": "od", "param": [151, 129]})
assert list(request) == [("class", "od"), ("param", ["151", "129"])]

Pass a request to dict() to get a plain Python mapping, useful for serialisation or inspection:

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:

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 dict(), adjusting selected values, and constructing a fresh request:

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:

from pymetkit import MarsRequest

request = MarsRequest("retrieve", {"class": "od", "param": "130"})
step = request["step"] if "step" in request else "0"
assert step == "0"

Equality and hashing#

Two requests are equal when they expand to the same result. The MARS language defines aliases, so "od" and "operational" for class refer to the same dataset — equality reflects that:

from pymetkit import MarsRequest

r1 = MarsRequest("retrieve", {"class": "od",          "date": "20230101", "param": "130"})
r2 = MarsRequest("retrieve", {"class": "operations",  "date": "20230101", "param": "130"})

assert r1 == r2

Expanding and validating#

expand() returns a new request expanded against the MARS language definition; validate() checks a request without inheriting defaults and raises MetKitException on invalid input.

from pymetkit import MarsRequest

request = MarsRequest(
    "retrieve",
    {
        "class": "od",
        "domain": "g",
        "date": "-1",
        "expver": "0001",
        "step": range(0, 13, 6),
    },
)

expanded = request.expand()          # inherit=True: fills in default values
assert expanded.verb() == "retrieve"

request.validate()                   # raises MetKitException if invalid

Merging requests#

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:

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"]

Parsing requests#

parse_mars_request() parses one or more requests from a string or a file-like object:

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"