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#
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.
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#
expand() returns one or more requests expanded
against the MARS language definition.
validate() checks a request without
inheriting defaults and raises MetKitException on invalid input.
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:
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#
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"]
Splitting requests#
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.
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#
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"