Metadata-Version: 2.4
Name: truewire-weather-gov
Version: 0.1.0
Summary: Typed, validated Python client for the US National Weather Service API, generated by Truewire.
License-Expression: MIT
Project-URL: Homepage, https://github.com/truewire-dev/weather-gov
Project-URL: Truewire, https://truewire.dev
Keywords: weather,nws,noaa,forecast,weather.gov,api-client,truewire
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: truewire-core<0.3,>=0.2.1
Provides-Extra: dev
Requires-Dist: truewire==0.9.1; extra == "dev"
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: pyright>=1.1.390; extra == "dev"
Requires-Dist: ruff>=0.8; extra == "dev"

# truewire-weather-gov

A typed, validated, async Python client for the
[US National Weather Service API](https://www.weather.gov/documentation/services-web-api):
forecasts, hourly forecasts, the raw gridded data behind both, station observations, and
every watch, warning and advisory in effect.

Generated by [Truewire](https://truewire.dev) from a spec of what the API puts on the wire,
and checked against recordings of real responses.

```sh
pip install truewire-weather-gov
```

No account, no key, no quota. The service asks one thing: say who you are in `User-Agent`.
That is the `contact` argument, and it is required — a default would be a lie about who is
calling.

**Note:** PyPI has an unrelated `weather-gov` package that also imports as `weather_gov`.
The two cannot share an environment.

## Use

Almost everything here is addressed by *grid cell* rather than by coordinates, so a caller
starts at `points.get_point`:

```python
import asyncio

from weather_gov import Weather


async def main() -> None:
  async with Weather.new(contact='you@example.com') as client:
    point = await client.points.get_point(latitude=47.6062, longitude=-122.3321)

    forecast = await client.forecast.get_forecast(
      office=point['gridId'], grid_x=point['gridX'], grid_y=point['gridY']
    )
    for period in forecast['periods'][:3]:
      print(period['name'], period['temperature'], period['temperatureUnit'], period['shortForecast'])


asyncio.run(main())
```

## Every measurement carries its unit

This API never sends a bare number. A temperature is
`{'unitCode': 'wmoUnit:degC', 'value': 13, 'qualityControl': 'V'}`, and `value` is `None`
wherever the measurement is missing rather than zero — an airport station reports
`windGust` only when there were gusts. The types say so, so the null is hard to forget.

## Walking observations safely

`stations.get_observations` is paged by time, and the walk knows something hand-written
code usually does not: the service caps a response at 500 observations and says nothing
about the ones it withheld. A full page raises `LogicError` rather than letting the walk
step past them.

## Everything else

Eleven endpoints across six groups, three response vocabularies (GeoJSON, schema.org,
JSON-LD) described as they actually arrive, and TypeScript and Rust clients from the same
spec.

Full documentation, the spec, and the recordings:
**https://github.com/truewire-dev/weather-gov**

MIT. NWS data is a work of the US government and is in the public domain.
