Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions oilpriceapi/resources/drilling.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
Drilling and completion activity data operations.
"""

from typing import Any, Dict, List
from typing import Any, Dict, List, cast


class DrillingIntelligenceResource:
"""Resource for drilling intelligence data."""

def __init__(self, client):
def __init__(self, client: Any) -> None:
"""Initialize drilling intelligence resource.

Args:
client: OilPriceAPI client instance
"""
self.client = client

def list(self, **params) -> List[Dict[str, Any]]:
def list(self, **params: Any) -> List[Dict[str, Any]]:
"""Get all drilling intelligence data.

Args:
Expand All @@ -40,8 +40,8 @@ def list(self, **params) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def latest(self) -> Dict[str, Any]:
"""Get latest drilling intelligence data.
Expand All @@ -61,8 +61,8 @@ def latest(self) -> Dict[str, Any]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(Dict[str, Any], response["data"])
return cast(Dict[str, Any], response)

def summary(self) -> Dict[str, Any]:
"""Get drilling intelligence summary.
Expand All @@ -82,10 +82,10 @@ def summary(self) -> Dict[str, Any]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(Dict[str, Any], response["data"])
return cast(Dict[str, Any], response)

def trends(self, **params) -> List[Dict[str, Any]]:
def trends(self, **params: Any) -> List[Dict[str, Any]]:
"""Get drilling activity trends.

Args:
Expand All @@ -107,10 +107,10 @@ def trends(self, **params) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def frac_spreads(self, **params) -> List[Dict[str, Any]]:
def frac_spreads(self, **params: Any) -> List[Dict[str, Any]]:
"""Get frac spread data.

Args:
Expand All @@ -132,10 +132,10 @@ def frac_spreads(self, **params) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def well_permits(self, **params) -> List[Dict[str, Any]]:
def well_permits(self, **params: Any) -> List[Dict[str, Any]]:
"""Get well permit data.

Args:
Expand All @@ -157,10 +157,10 @@ def well_permits(self, **params) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def duc_wells(self, **params) -> List[Dict[str, Any]]:
def duc_wells(self, **params: Any) -> List[Dict[str, Any]]:
"""Get DUC (Drilled but Uncompleted) wells data.

Args:
Expand All @@ -182,10 +182,10 @@ def duc_wells(self, **params) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def completions(self, **params) -> List[Dict[str, Any]]:
def completions(self, **params: Any) -> List[Dict[str, Any]]:
"""Get well completion data.

Args:
Expand All @@ -207,10 +207,10 @@ def completions(self, **params) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def wells_drilled(self, **params) -> List[Dict[str, Any]]:
def wells_drilled(self, **params: Any) -> List[Dict[str, Any]]:
"""Get wells drilled data.

Args:
Expand All @@ -232,8 +232,8 @@ def wells_drilled(self, **params) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def basin(self, name: str) -> Dict[str, Any]:
"""Get drilling data for a specific basin.
Expand All @@ -256,5 +256,5 @@ def basin(self, name: str) -> Dict[str, Any]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(Dict[str, Any], response["data"])
return cast(Dict[str, Any], response)
26 changes: 13 additions & 13 deletions oilpriceapi/resources/forecasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
EIA and agency price forecast operations.
"""

from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, cast


class ForecastsResource:
"""Resource for official price forecasts from EIA and other agencies."""

def __init__(self, client):
def __init__(self, client: Any) -> None:
"""Initialize forecasts resource.

Args:
Expand All @@ -35,7 +35,7 @@ def monthly(self, commodity: Optional[str] = None) -> Dict[str, Any]:
>>> # Specific commodity
>>> wti_forecasts = client.forecasts.monthly(commodity="WTI_USD")
"""
params = {}
params: Dict[str, Any] = {}
if commodity:
params["commodity"] = commodity

Expand All @@ -47,8 +47,8 @@ def monthly(self, commodity: Optional[str] = None) -> Dict[str, Any]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(Dict[str, Any], response["data"])
return cast(Dict[str, Any], response)

def accuracy(self) -> Dict[str, Any]:
"""Get forecast accuracy metrics.
Expand All @@ -69,8 +69,8 @@ def accuracy(self) -> Dict[str, Any]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(Dict[str, Any], response["data"])
return cast(Dict[str, Any], response)

def archive(self, year: Optional[int] = None) -> List[Dict[str, Any]]:
"""Get archived forecasts.
Expand All @@ -86,7 +86,7 @@ def archive(self, year: Optional[int] = None) -> List[Dict[str, Any]]:
>>> for forecast in archive:
... print(f"{forecast['date']}: {forecast['commodity']} = ${forecast['price']:.2f}")
"""
params = {}
params: Dict[str, Any] = {}
if year:
params["year"] = year

Expand All @@ -98,8 +98,8 @@ def archive(self, year: Optional[int] = None) -> List[Dict[str, Any]]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(List[Dict[str, Any]], response["data"])
return cast(List[Dict[str, Any]], response)

def get(self, period: str, commodity: Optional[str] = None) -> Dict[str, Any]:
"""Get forecast for a specific period.
Expand All @@ -116,7 +116,7 @@ def get(self, period: str, commodity: Optional[str] = None) -> Dict[str, Any]:
>>> print(f"March 2025 Brent Forecast: ${forecast['price']:.2f}")
>>> print(f"Range: ${forecast['low']:.2f} - ${forecast['high']:.2f}")
"""
params = {}
params: Dict[str, Any] = {}
if commodity:
params["commodity"] = commodity

Expand All @@ -128,5 +128,5 @@ def get(self, period: str, commodity: Optional[str] = None) -> Dict[str, Any]:

# Parse response
if "data" in response:
return response["data"]
return response
return cast(Dict[str, Any], response["data"])
return cast(Dict[str, Any], response)
75 changes: 73 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ dev = [
"pytest-asyncio>=0.21.0",
"pytest-timeout>=2.1.0",
"black>=23.0.0",
"mypy>=1.0.0",
# Pin <3 defensively: mypy 2.x tightened defaults and rejected the old
# python_version="3.8" config, which contributed to CI going red.
"mypy>=1.0.0,<3",
"ruff>=0.0.261",
"pre-commit>=3.0.0",
]
Expand Down Expand Up @@ -117,7 +119,10 @@ ignore = [
fixable = ["ALL"]

[tool.mypy]
python_version = "3.8"
# Type-check target; mypy 2.x requires >=3.10 (it rejects "3.8"). The package
# itself still supports Python >=3.8 (see requires-python above); python_version
# here only controls which language features mypy assumes when type-checking.
python_version = "3.10"
strict = true
warn_return_any = true
warn_unused_configs = true
Expand All @@ -127,6 +132,72 @@ show_error_codes = true
show_column_numbers = true
pretty = true

# --- Gradual typing baseline (CI was non-blocking for mypy until v1.7.0) ---
# The SDK has not been fully annotated yet. Until v1.7.0 the mypy CI step ran
# with `continue-on-error: true`, so ~700 "annotation completeness" findings
# (missing annotations / returning Any) never blocked merges. v1.7.0 made the
# step blocking, turning CI red. Rather than hide everything, we:
# 1. Relax ONLY the annotation-completeness checks package-wide (the legitimate
# "we haven't finished typing the SDK" debt), keeping every bug-catching
# check (assignment, arg-type, union-attr, etc.) active.
# 2. Re-enable FULL strict checking for modules that are already clean
# (forecasts, drilling) so they cannot regress.
# 3. Grandfather the handful of legacy modules that still trip real-bug codes
# via narrow per-module overrides (TODO: fix and remove these).
[[tool.mypy.overrides]]
module = "oilpriceapi.*"
disable_error_code = ["no-untyped-def", "no-any-return", "no-untyped-call"]

# Modules already fully strict-clean — keep them strict so they can't regress.
[[tool.mypy.overrides]]
module = ["oilpriceapi.resources.forecasts", "oilpriceapi.resources.drilling"]
disable_error_code = []

# TODO(typing-debt): fix the real type issues below, then delete these overrides.
[[tool.mypy.overrides]]
module = "oilpriceapi"
disable_error_code = ["assignment"]

[[tool.mypy.overrides]]
module = "oilpriceapi.client"
disable_error_code = ["assignment", "arg-type", "misc", "type-arg"]

[[tool.mypy.overrides]]
module = "oilpriceapi.async_client"
disable_error_code = ["assignment", "attr-defined", "call-overload", "misc", "type-arg"]

[[tool.mypy.overrides]]
module = "oilpriceapi.cli"
disable_error_code = ["arg-type", "attr-defined", "no-redef", "union-attr"]

[[tool.mypy.overrides]]
module = "oilpriceapi.models"
disable_error_code = ["import-untyped"]

[[tool.mypy.overrides]]
module = "oilpriceapi.telemetry"
disable_error_code = ["arg-type", "assignment", "attr-defined", "var-annotated"]

[[tool.mypy.overrides]]
module = "oilpriceapi.resources.alerts"
disable_error_code = ["assignment"]

[[tool.mypy.overrides]]
module = "oilpriceapi.resources.analytics"
disable_error_code = ["assignment"]

[[tool.mypy.overrides]]
module = "oilpriceapi.resources.data_sources"
disable_error_code = ["assignment"]

[[tool.mypy.overrides]]
module = "oilpriceapi.resources.webhooks"
disable_error_code = ["assignment"]

[[tool.mypy.overrides]]
module = "oilpriceapi.resources.prices"
disable_error_code = ["union-attr"]

[tool.pytest.ini_options]
minversion = "7.0"
testpaths = ["tests"]
Expand Down
Loading