Skip to main content

Universal weather forecast aggregation library with a typed normalized schema.

Project description

omni-weather-forecast-apis

PyPI CI Ruff Checked with pyrefly License Python 3.13+ Ask DeepWiki

Async Python library that fans out forecast requests across multiple weather providers and normalizes the results into one typed Pydantic schema. It preserves provider-native cadence and time boundaries while converting units and condition codes into a common representation.

Requires Python 3.13 or newer.

๐Ÿ“– Documentation site

Contents

Features

  • Multi-provider fan-out with async orchestration and partial-failure tolerance
  • Typed normalized schema โ€” common Pydantic models for minutely, hourly, daily, and alert data
  • Plugin architecture โ€” 13 providers with typed per-provider config validation
  • Resilient by default โ€” retries with exponential backoff (honoring Retry-After), conditional-request HTTP caching (ETag/Last-Modified/Expires), and explicit connection pool limits
  • Rate limiting and quotas โ€” global concurrency and RPS limits with per-provider overrides, plus per-provider daily quota caps
  • Secrets from the environment โ€” reference API keys as ${ENV_VAR} placeholders instead of embedding them in config files
  • CLI โ€” loads a TOML config, queries providers, prints a table or JSON, and optionally persists normalized output to SQLite
  • Extensible โ€” response hooks and a documented SQLite feature view for downstream ensemble/verification projects

Supported Providers

Three of the thirteen providers need no API key at all, so you can try the library without signing up for anything.

Provider Plugin ID API key Minutely Hourly Daily Alerts Multi-model Coverage
Open-Meteo open_meteo Optional 1 h 16 d 16 d โ€” โœ… Global
MET Norway met_norway None โ€” 9 d โ€” โ€” โ€” Nordics
NWS / NOAA nws None โ€” โœ… โœ… โœ… โ€” US only
OpenWeather openweather Required 1 h 48 h 8 d โœ… โ€” Global
WeatherAPI weatherapi Required โ€” 14 d 14 d โœ… โ€” Global
Tomorrow.io tomorrow_io Required 1 h 5 d 6 d โ€” โ€” Global
Visual Crossing visual_crossing Required โ€” 15 d 15 d โœ… โ€” Global
Weatherbit weatherbit Required โ€” 10 d 16 d โ€” โ€” Global
Meteosource meteosource Required 1 h 7 d 30 d โœ… โ€” Global
Pirate Weather pirate_weather Required 1 h 48 h 8 d โœ… โ€” Global
Stormglass stormglass Required โ€” โœ… โ€” โ€” โœ… Global
Weather Unlocked weather_unlocked Required โ€” โœ… โœ… โ€” โ€” Global
Google Weather google_weather Required โ€” 10 d 10 d โ€” โ€” Global

The minutely, hourly, and daily columns give each provider's maximum forecast horizon. โœ… means the granularity is supported but the plugin declares no horizon bound, and โ€” means it is not supported at all. Multi-model providers return several independent forecasts per request โ€” Open-Meteo exposes named numerical weather models (best_match, ecmwf_ifs025, โ€ฆ) and Stormglass returns multiple upstream sources โ€” which is what makes them useful for ensembles.

MET Norway and NWS additionally require a user_agent identifying your application; Weather Unlocked uses an app_id + app_key pair rather than a single key. Pirate Weather's hourly horizon extends to 168 h when extend_hourly = true. See the provider configuration reference for every key each plugin accepts.

Quick Start

Open-Meteo, MET Norway, and NWS need no API keys, so this runs end to end without any signup. No install step โ€” uv fetches the package into a throwaway environment:

cat > config.toml << 'EOF'
[[providers]]
plugin_id = "open_meteo"
config = { models = ["best_match", "ecmwf_ifs025"] }

[[providers]]
plugin_id = "met_norway"
config = { user_agent = "MyApp/1.0 you@yourdomain.com" }

[[providers]]
plugin_id = "nws"
config = { user_agent = "MyApp/1.0 you@yourdomain.com" }
EOF

uvx --from "omni-weather-forecast-apis[cli]" omni-weather \
  --config ./config.toml \
  --lat 40.7128 \
  --lon -74.0060 \
  --sqlite ./forecasts.sqlite

Put a real contact address in user_agent before running this. MET Norway's terms require one to identify the caller, and their API rejects placeholder domains such as example.com with a 403 Forbidden.

Which prints:

                              Run 1 โ€” 3/3 succeeded
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
โ”ƒ Provider   โ”ƒ Status โ”ƒ Latency โ”ƒ Hourly โ”ƒ Daily โ”ƒ Minutely โ”ƒ Alerts โ”ƒ Detail      โ”ƒ
โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
โ”‚ open_meteo โ”‚   OK   โ”‚   924ms โ”‚    336 โ”‚    14 โ”‚        0 โ”‚      - โ”‚ 2 source(s) โ”‚
โ”‚ met_norway โ”‚   OK   โ”‚   705ms โ”‚     90 โ”‚     0 โ”‚        0 โ”‚      - โ”‚ 1 source(s) โ”‚
โ”‚ nws        โ”‚   OK   โ”‚   283ms โ”‚    156 โ”‚     8 โ”‚        0 โ”‚      - โ”‚ 1 source(s) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       Saved to forecasts.sqlite in 924ms

Open-Meteo contributes two sources because two models were requested. Every forecast is now in forecasts.sqlite, normalized and ready to query.

Installation

# As a library dependency
pip install omni-weather-forecast-apis

# With CLI niceties โ€” rich tables and loguru debug logging
pip install "omni-weather-forecast-apis[cli]"

# Run the CLI without installing anything
uvx --from "omni-weather-forecast-apis[cli]" omni-weather --help

Requires Python 3.13 or newer. The CLI works without the cli extra: table output falls back to plain text and --debug falls back to stdlib logging.

Contributing to this repository instead? See Development for the uv sync workflow.

How It Works

  1. Fan-out โ€” A ForecastRequest is dispatched concurrently to every enabled provider using async tasks, bounded by configurable concurrency and rate limits.
  2. Normalize โ€” Each provider plugin converts its native response into the common SourceForecast schema, translating units (e.g. Fahrenheit to Celsius, mph to m/s) and mapping provider-specific condition codes to a shared WeatherCondition enum.
  3. Aggregate โ€” Results are collected into a single ForecastResponse. Providers that succeed return ProviderSuccess with their forecasts; providers that fail return ProviderError with a typed error code. The response always completes, even if some providers fail.

Configuration

The client and CLI both use a TOML configuration file that matches OmniWeatherConfig.

latitude = 40.7128
longitude = -74.0060
sqlite = "forecasts.sqlite"
granularity = ["hourly", "daily"]
language = "en"
include_raw = false
debug = false
default_timeout_ms = 10000

[rate_limiting]
max_in_flight = 10
max_requests_per_second = 20

[retry]
max_attempts = 3          # total attempts per provider fetch; 1 disables retries
initial_backoff_ms = 500
max_backoff_ms = 8000
backoff_multiplier = 2.0
jitter = true

[http]
max_connections = 20
max_keepalive_connections = 10
connect_timeout_ms = 5000
cache_enabled = true      # conditional-request HTTP cache (ETag/Last-Modified/Expires)
cache_max_entries = 256

[[providers]]
plugin_id = "open_meteo"
enabled = true
config = { models = ["best_match", "ecmwf_ifs025"] }

[[providers]]
plugin_id = "met_norway"
enabled = true
config = { user_agent = "MyApp/1.0 you@yourdomain.com", variant = "complete" }

[[providers]]
plugin_id = "openweather"
enabled = true
config = { api_key = "${OPENWEATHER_API_KEY}", units = "metric" }
rate_limit_rps = 5
timeout_ms = 8000
max_requests_per_day = 900

Retries

Transient failures โ€” network errors, timeouts, and HTTP 429 rate limits โ€” are retried with exponential backoff and jitter. A server-provided Retry-After header is honored; retries are abandoned when it exceeds 60 seconds. Non-transient failures such as auth errors are never retried. Set a per-provider retry table on a registration to override the global policy.

HTTP caching and connection limits

The shared HTTP client is powered by HTTPX2, uses explicit connection pool limits and a connect timeout, and caches GET responses in memory. Fresh responses (Cache-Control: max-age / Expires) are served without a network round-trip; stale responses carrying ETag/Last-Modified validators are revalidated with conditional requests and reused on 304 Not Modified. Responses that declare Vary are only reused for requests sending the same values for the named headers (Vary: * is never cached). Requests carrying Authorization or Cookie headers bypass the shared cache. MET Norway's terms of service require conditional requests and the NWS strongly encourages caching. Disable with cache_enabled = false under [http].

Daily quotas

Most free tiers are capped per day, not per second. Set max_requests_per_day on a provider registration and the client returns a quota_exceeded error once the day's budget (UTC) is spent instead of burning through it. Each fetch attempt counts one request โ€” retries are real HTTP calls against the provider's cap, so a single forecast() call may consume up to retry.max_attempts units when transient failures trigger retries. This deliberately mirrors provider-side accounting; lower max_attempts if you need a tighter bound per call. The CLI persists counts in the SQLite database (provider_quota_usage table) so limits survive across runs; library users can pass any QuotaTracker implementation with atomic try_consume support (InMemoryQuotaTracker is the default, SqliteQuotaTracker is bundled in omni_weather_forecast_apis.quota).

API keys from environment variables

Any string value inside a provider config block can reference an environment variable instead of embedding a secret:

# whole-string reference
config = { api_key = "${OPENWEATHER_API_KEY}" }

# explicit marker table (equivalent)
config = { api_key = { env = "OPENWEATHER_API_KEY" } }

Resolution happens at client initialization and recurses through nested tables and arrays. A placeholder naming an unset variable becomes a per-provider initialization error; other providers are unaffected. Partial interpolation ("prefix-${VAR}") is not supported โ€” only whole-string placeholders are resolved.

Library Usage

import asyncio

from omni_weather_forecast_apis import (
    ForecastRequest,
    Granularity,
    OmniWeatherConfig,
    ProviderError,
    ProviderRegistration,
    ProviderId,
    ProviderSuccess,
    create_omni_weather,
)


async def main() -> None:
    config = OmniWeatherConfig(
        providers=[
            ProviderRegistration(
                plugin_id=ProviderId.OPEN_METEO,
                config={"models": ["best_match"]},
            ),
            ProviderRegistration(
                plugin_id=ProviderId.MET_NORWAY,
                config={"user_agent": "MyApp/1.0 you@yourdomain.com"},
            ),
        ],
    )

    async with await create_omni_weather(config) as client:
        response = await client.forecast(
            ForecastRequest(
                latitude=34.2484,
                longitude=-117.1931,
                granularity=[Granularity.HOURLY, Granularity.DAILY],
            ),
        )
        print(response.summary)
        # ForecastResponseSummary(total=2, succeeded=2, failed=0)

        for result in response.results:
            match result:
                case ProviderSuccess(provider=pid, forecasts=forecasts):
                    for fc in forecasts:
                        for pt in fc.hourly:
                            print(f"{pid} {pt.timestamp}: {pt.temperature}ยฐC, {pt.condition}")
                case ProviderError(provider=pid, error=err):
                    print(f"{pid} failed: {err.code} โ€” {err.message}")


asyncio.run(main())

Example output:

ProviderId.OPEN_METEO 2026-03-13 18:00:00+00:00: 12.3ยฐC, WeatherCondition.PARTLY_CLOUDY
ProviderId.OPEN_METEO 2026-03-13 19:00:00+00:00: 11.8ยฐC, WeatherCondition.OVERCAST
ProviderId.MET_NORWAY 2026-03-13 18:00:00+00:00: 12.1ยฐC, WeatherCondition.RAIN
...

CLI Usage

omni-weather \
  --config ./config.toml \
  --lat 34.2484 \
  --lon -117.1931 \
  --sqlite ./forecasts.sqlite

# Query only specific providers
omni-weather \
  --config ./config.toml \
  --lat 34.2484 \
  --lon -117.1931 \
  --sqlite ./forecasts.sqlite \
  --provider open_meteo \
  --provider nws \
  --granularity hourly

# Emit the full normalized response as JSON (no SQLite required)
omni-weather \
  --config ./config.toml \
  --lat 34.2484 \
  --lon -117.1931 \
  --format json | jq '.results[] | {provider, status}'

# Pipe flattened per-point rows into data tools
omni-weather --config ./config.toml --lat 34.2 --lon -117.2 \
  --format csv > forecast.csv
omni-weather --config ./config.toml --lat 34.2 --lon -117.2 \
  --format ndjson | jq 'select(.type == "forecast_point") | .temperature'

csv and ndjson emit one row/line per forecast data point, flattened with provider, model, and granularity (minutely / hourly / daily) columns followed by the normalized point fields. ndjson lines carry a type field (forecast_point, alert, or provider_error); CSV omits alerts (noted on stderr) and reports provider errors on stderr only.

Flag Required Default Description
--config PATH No ~/.config/omni_weather_forecast_apis.toml Path to TOML configuration file
--lat FLOAT No config value Latitude (-90 to 90); overrides config
--lon FLOAT No config value Longitude (-180 to 180); overrides config
--sqlite PATH No config value SQLite database output path; overrides config. Persistence is skipped when neither is set
--format FMT No table Output format: table (human-readable summary), json (full normalized response), csv (one flattened row per data point), or ndjson (one JSON object per line, typed forecast_point / alert / provider_error)
--provider ID No all enabled Restrict to specific provider(s); repeatable
--granularity GRAN No config value minutely, hourly, or daily; repeatable
--language LANG No config value Provider language preference
--include-raw No config value Persist raw provider payloads
--timeout-ms MS No config value Override the default timeout; provider-specific timeouts still take precedence
--debug No config value Enable verbose debug output to stderr and write a .log file next to the SQLite database, or ./omni-weather.log when SQLite is omitted

Exit codes: 0 all providers succeeded, 1 at least one provider failed, 2 invalid arguments or configuration/load error.

Observability

Beyond the structured per-provider log events (log_hooks), the client emits typed metric events for every request attempt, retry, HTTP cache lookup, and quota consumption. Register any callable as a MetricsHook โ€” no extra dependencies required:

from omni_weather_forecast_apis import MetricEvent, MetricKind, create_omni_weather

def record(event: MetricEvent) -> None:
    if event.kind is MetricKind.REQUEST_END:
        print(event.provider, event.latency_ms, event.error_code)

client = await create_omni_weather(config, metrics_hooks=[record])

MetricKind covers request_start, request_end, retry_scheduled, cache_hit, cache_miss, quota_consumed, and quota_exhausted. Cache events carry the request url instead of a provider (the HTTP cache is shared across providers). response.summary.retries reports how many retries a forecast() call needed.

For OpenTelemetry, install the otel extra and use the prebuilt bridge:

pip install "omni-weather-forecast-apis[otel]"
from omni_weather_forecast_apis.otel import create_otel_metrics_hook

client = await create_omni_weather(
    config,
    metrics_hooks=[create_otel_metrics_hook()],
)

The bridge records counters for requests, retries, cache outcomes, and quota, plus a omni_weather.request.duration_ms histogram.

Partial Failures

The library is designed for partial-failure tolerance. When some providers fail (network errors, rate limits, auth issues), the response still completes with results from the providers that succeeded.

Each entry in response.results is either a ProviderSuccess or ProviderError, distinguished by the status field. The response.summary provides counts at a glance:

response.summary
# ForecastResponseSummary(total=3, succeeded=2, failed=1)

ProviderError includes a typed error.code (AUTH_FAILED, RATE_LIMITED, QUOTA_EXCEEDED, TIMEOUT, NETWORK, PARSE, NOT_AVAILABLE, UNKNOWN), a human-readable error.message, the error.http_status when available, and error.latency_ms for how long the request ran before failing.

The CLI reflects this in exit codes: 0 means all providers succeeded, 1 means at least one failed (but partial results are still written to SQLite), and 2 means invalid arguments or a configuration/load error.

Normalized Schema

All provider responses are normalized into a common set of Pydantic models. Units are standardized: temperatures in ยฐC, wind speeds in m/s, pressure in hPa, precipitation in mm, visibility in km.

WeatherDataPoint (hourly)

Field Type Unit
temperature, apparent_temperature, dew_point float | None ยฐC
humidity float | None % (0-100)
wind_speed, wind_gust float | None m/s
wind_direction float | None degrees
pressure_sea, pressure_surface float | None hPa
precipitation, rain, snow, snow_depth float | None mm
precipitation_probability float | None 0-1
cloud_cover, cloud_cover_low, cloud_cover_mid, cloud_cover_high float | None %
visibility float | None km
uv_index float | None 0-11+
solar_radiation_ghi, solar_radiation_dni, solar_radiation_dhi float | None W/mยฒ
condition WeatherCondition | None enum
is_day bool | None

DailyDataPoint

Field Type Unit
date date
temperature_max, temperature_min float | None ยฐC
apparent_temperature_max, apparent_temperature_min float | None ยฐC
wind_speed_max, wind_gust_max float | None m/s
precipitation_sum, rain_sum, snowfall_sum float | None mm
precipitation_probability_max float | None 0-1
cloud_cover_mean float | None %
humidity_mean float | None %
uv_index_max float | None 0-11+
sunrise, sunset, moonrise, moonset datetime | None UTC
moon_phase float | None 0-1
daylight_duration float | None seconds
condition WeatherCondition | None enum
summary str | None

MinutelyDataPoint

Field Type Unit
precipitation_intensity float | None mm/h
precipitation_probability float | None 0-1

WeatherAlert

Field Type
sender_name str
event str
start, end datetime (UTC)
description str
severity EXTREME | SEVERE | MODERATE | MINOR | UNKNOWN
url str | None

WeatherCondition enum

CLEAR, MOSTLY_CLEAR, PARTLY_CLOUDY, MOSTLY_CLOUDY, OVERCAST, FOG, DRIZZLE, LIGHT_RAIN, RAIN, HEAVY_RAIN, FREEZING_RAIN, LIGHT_SNOW, SNOW, HEAVY_SNOW, SLEET, HAIL, THUNDERSTORM, THUNDERSTORM_RAIN, THUNDERSTORM_HEAVY, DUST, SAND, SMOKE, HAZE, TORNADO, HURRICANE, UNKNOWN

Provider Configuration Reference

Each provider accepts a typed config dict. Required fields are marked with bold.

Provider Config Keys
open_meteo api_key?, models (default: ["best_match"]), extra_hourly_vars?, extra_daily_vars?
met_norway user_agent, altitude?, variant ("compact" | "complete", default: "complete")
nws user_agent, grid_override? ({office, grid_x, grid_y})
openweather api_key, exclude?, units ("standard" | "metric" | "imperial", default: "metric")
weatherapi api_key, days (1-14, default: 7), aqi (default: false), alerts (default: true)
tomorrow_io api_key, fields?
visual_crossing api_key, include (default: "hours,days,alerts")
weatherbit api_key, hours (1-240, default: 48), units ("M" | "S" | "I", default: "M")
meteosource api_key, sections (default: ["current", "hourly", "daily"])
pirate_weather api_key, extend_hourly (default: false), version ("1" | "2", default: "2")
stormglass api_key, sources (default: ["sg"]), params (list of weather variables)
weather_unlocked app_id, app_key, lang?
google_weather api_key, hours (1-240, default: 48), days (1-10, default: 10)

SQLite Output

The CLI creates a normalized database with these tables:

Table Contents
forecast_runs Request metadata per invocation
provider_results One row per provider outcome (success or error)
source_forecasts One row per model/source forecast within a provider
minutely_points Precipitation intensity at minute intervals
hourly_points Normalized hourly forecast rows
daily_points Normalized daily summary rows
alerts Weather alerts and warnings
provider_logs Per-provider lifecycle log entries (start, retry, success, error) per run
provider_quota_usage Requests per provider per UTC day, used for daily quota enforcement

The stacking_features SQL view joins hourly points with their provider, model, run cycle, and forecast horizon โ€” a ready-made feature matrix for downstream ensemble/verification work. Every provider that forecast the same valid time lines up on one axis:

SELECT provider, model, ROUND(temperature, 1) AS temp_c, ROUND(wind_speed, 1) AS wind_ms
FROM stacking_features
WHERE CAST(horizon_hours AS INT) = 24
ORDER BY provider, model;
provider    model         temp_c  wind_ms
----------  ------------  ------  -------
met_norway  met_norway    23.2    4.5
nws         nws           25.0    4.5
open_meteo  best_match    22.0    5.1
open_meteo  ecmwf_ifs025  23.2    3.3

Four independent 24-hour-ahead forecasts for the same point and time, already unit-normalized โ€” the input a blending or verification model wants. run_cycle and horizon_hours let you group by forecast age, and fetched_at_unix distinguishes successive runs.

Extending

Consensus/ensemble forecasting and forecast verification are intended to live in separate packages built on three extension points โ€” see the Extending guide for details:

  • Response hooks โ€” sync or async callables that receive every completed ForecastResponse:

    async def record_for_verification(response: ForecastResponse) -> None:
        ...
    
    client = await create_omni_weather(config, response_hooks=[record_for_verification])
    
  • Custom provider plugins โ€” pass any WeatherPlugin implementations to create_omni_weather(config, plugins=[...]) for a per-client plugin set, or register globally with omni_weather_forecast_apis.plugins.register_plugin (the global registry backs the CLI and is the fallback when plugins is omitted).

  • The SQLite feature matrix โ€” query the stacking_features view for aligned per-provider hourly forecasts with horizons and run cycles.

Documentation

The documentation site is built with Zensical (configured in zensical.toml) from the docs/ directory:

uv sync --group docs
uv run zensical serve

Development

# Set up the repository (Python 3.13+)
uv sync

# Lint, format, and type-check
uv run ruff check src --fix
uv run ruff format src tests
uv run pyrefly check src
uv run ty check src

# Dependency, package, and complexity checks
uv run deptry src
uv run pyroma --min 8 .
uv run lizard -Eduplicate -C 27 src

# Tests and the 87% coverage floor
uv run pytest tests/ --cov=src --cov-report=term-missing

License

Apache 2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

omni_weather_forecast_apis-0.3.0.tar.gz (66.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

omni_weather_forecast_apis-0.3.0-py3-none-any.whl (90.2 kB view details)

Uploaded Python 3

File details

Details for the file omni_weather_forecast_apis-0.3.0.tar.gz.

File metadata

  • Download URL: omni_weather_forecast_apis-0.3.0.tar.gz
  • Upload date:
  • Size: 66.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for omni_weather_forecast_apis-0.3.0.tar.gz
Algorithm Hash digest
SHA256 441edee5db3c367c328b0cda00cc7786edd811a85cc9b408c207045c8e513a0c
MD5 a800300905447c3bfb219a304a874690
BLAKE2b-256 68ede7ff7def13521c2ae438e5237be21403611e834fcb7ed5f6e9a256c4a7be

See more details on using hashes here.

File details

Details for the file omni_weather_forecast_apis-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: omni_weather_forecast_apis-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 90.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for omni_weather_forecast_apis-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4c540deaa9eeb3e737652fb7e365f08bc0ea997edbd6d2b8eebc55851ebdccd6
MD5 dc24bd7d147010fab24d288cac585876
BLAKE2b-256 91ad1a1b2bf90991c7f7744159e2b565f2ac5d0e207dbd055ff55e8e01e44668

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page