diff --git a/.github/workflows/weekly-health.yml b/.github/workflows/weekly-health.yml index f910ecf..b7cb43b 100644 --- a/.github/workflows/weekly-health.yml +++ b/.github/workflows/weekly-health.yml @@ -25,11 +25,24 @@ jobs: python -m pip install --upgrade pip pip install -e '.[dev]' + # OILPRICEAPI_TEST_KEY currently resolves empty at runtime (see PR). + # Guard at the shell level so the job passes loudly (::warning::) instead + # of failing or silently skipping every test. - name: Run integration tests - run: pytest tests/ -m 'integration' -v --timeout=60 + run: | + if [ -z "$OILPRICEAPI_KEY" ]; then + echo "::warning::OILPRICEAPI_TEST_KEY secret is empty/unset - live integration tests skipped" + exit 0 + fi + pytest tests/ -m 'integration' -v --no-cov --timeout=60 - name: Run contract tests - run: pytest tests/ -m 'contract' -v --timeout=60 + run: | + if [ -z "$OILPRICEAPI_KEY" ]; then + echo "::warning::OILPRICEAPI_TEST_KEY secret is empty/unset - contract tests skipped" + exit 0 + fi + pytest tests/ -m 'contract' -v --no-cov --timeout=60 - name: Check for dependency vulnerabilities run: | diff --git a/README.md b/README.md index 70dd7bb..4b60598 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,28 @@ for price in prices: print(f"{price.commodity}: ${price.value:.2f}") ``` +### Beyond Oil — Gas, LNG, Carbon & Fuels + +The same client covers EU ETS carbon, European gas (TTF), LNG (JKM), and marine/road/aviation fuels — for maritime compliance (EU ETS / FuelEU Maritime), fleet & logistics fuel costing, LNG and European gas analytics, and CBAM reporting. + +```python +# EU ETS carbon allowances (EUAs) — spot price in EUR +eua = client.prices.get("EU_CARBON_EUR") +print(f"EU carbon: €{eua.value:.2f}") # €XX.XX per tonne CO2 + +# Dutch TTF natural gas — futures curve via slug helpers +# (other slugs: "lng-jkm", "eua-carbon", "uk-carbon", "natural-gas") +ttf = client.futures.latest("ttf-gas") +print(ttf["front_month"]["last_price"]) # front-month, €XX.XX/MWh + +# Marine bunker fuels (VLSFO / MGO / IFO380) at a specific port +rotterdam = client.bunker_fuels.port("RTM") # 3-letter port codes: SIN, RTM, FUJ, ... +for fuel in rotterdam["prices"]: + print(f"{fuel['grade']}: ${fuel['price']}/{fuel['unit']}") # VLSFO: $XXX.XX/MT +``` + +Spot codes for these markets also work with `client.prices.get()` / `get_multiple()`: `DUTCH_TTF_EUR`, `JKM_LNG_USD`, `VLSFO_USD`, `JET_FUEL_USD`, `DIESEL_USD`, `NATURAL_GAS_USD`. (Futures and bunker endpoints require a plan with futures data — spot prices work on every tier.) + ### Historical Data with Pandas ```python diff --git a/tests/contract/conftest.py b/tests/contract/conftest.py index 53ce388..7b7e19e 100644 --- a/tests/contract/conftest.py +++ b/tests/contract/conftest.py @@ -5,7 +5,14 @@ import os import pytest from pathlib import Path -from dotenv import dotenv_values +try: + from dotenv import dotenv_values +except ImportError: + # python-dotenv is optional (not in the [dev] extra). In CI the API key + # comes from the environment, so don't make the contract suite + # uncollectable when dotenv isn't installed. + def dotenv_values(_path): # type: ignore[misc] + return {} from oilpriceapi import OilPriceAPI # Load .env file from project root @@ -16,15 +23,17 @@ env_vars = dotenv_values(env_path) # Get API credentials -API_KEY = env_vars.get('OILPRICEAPI_KEY') -BASE_URL = env_vars.get('OILPRICEAPI_BASE_URL', 'https://api.oilpriceapi.com') +# Prefer .env for local dev, fall back to the environment for CI +# (weekly-health.yml sets OILPRICEAPI_KEY from the OILPRICEAPI_TEST_KEY secret). +API_KEY = env_vars.get('OILPRICEAPI_KEY') or os.environ.get('OILPRICEAPI_KEY') +BASE_URL = env_vars.get('OILPRICEAPI_BASE_URL') or os.environ.get('OILPRICEAPI_BASE_URL', 'https://api.oilpriceapi.com') @pytest.fixture(scope="session") def api_key(): """Provide API key for tests.""" if not API_KEY: - pytest.skip("OILPRICEAPI_KEY not found in .env file") + pytest.skip("OILPRICEAPI_KEY not found in .env file or environment") return API_KEY diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 83b63d6..0d3916d 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -26,8 +26,10 @@ def dotenv_values(_path): # type: ignore[misc] env_vars = dotenv_values(env_path) # Get API credentials -API_KEY = env_vars.get('OILPRICEAPI_KEY') -BASE_URL = env_vars.get('OILPRICEAPI_BASE_URL', 'https://api.oilpriceapi.com') +# Prefer .env for local dev, fall back to the environment for CI +# (weekly-health.yml sets OILPRICEAPI_KEY from the OILPRICEAPI_TEST_KEY secret). +API_KEY = env_vars.get('OILPRICEAPI_KEY') or os.environ.get('OILPRICEAPI_KEY') +BASE_URL = env_vars.get('OILPRICEAPI_BASE_URL') or os.environ.get('OILPRICEAPI_BASE_URL', 'https://api.oilpriceapi.com') # CI shares a single 1-request/second API key across repositories, so live # tests can collide and get HTTP 429 (RateLimitError) through no fault of the @@ -52,7 +54,7 @@ def _throttle_live_calls(): def api_key(): """Provide API key for tests.""" if not API_KEY: - pytest.skip("OILPRICEAPI_KEY not found in .env file") + pytest.skip("OILPRICEAPI_KEY not found in .env file or environment") return API_KEY