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
42 changes: 34 additions & 8 deletions mlbstatsapi/mlb_dataadapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
MlbTransportError,
)
from .warnings import MlbHttpCompatibilityWarning
import inspect
import logging
import warnings

Expand All @@ -28,9 +29,33 @@
# Bounded excerpt for error response bodies attached to MlbHttpError.
HTTP_ERROR_BODY_EXCERPT_LIMIT = 500

# Frames from warnings.warn() out to whoever called MlbDataAdapter.get(), so the
# warning points at application code rather than the helper below.
COMPATIBILITY_WARNING_STACKLEVEL = 3

def _is_mlbstatsapi_module(module_name: str) -> bool:
"""Return True when *module_name* belongs to this package."""
return module_name == "mlbstatsapi" or module_name.startswith("mlbstatsapi.")


def _compatibility_warning_stacklevel() -> int:
"""Return a warnings.warn stacklevel for the first non-package caller.

A fixed stack level cannot serve both direct MlbDataAdapter.get() calls and
public Mlb endpoint methods that wrap the adapter. Walk frames from the
caller of this helper outward and stop at the first module outside the
mlbstatsapi package namespace.
"""
frame = inspect.currentframe()
stacklevel = 1
try:
frame = frame.f_back
while frame is not None:
module_name = frame.f_globals.get("__name__", "")
if not _is_mlbstatsapi_module(module_name):
return stacklevel
stacklevel += 1
frame = frame.f_back
finally:
del frame
return 1


def _warn_http_compatibility(
Expand All @@ -45,13 +70,14 @@ def _warn_http_compatibility(
"""
warnings.warn(
(
f"HTTP {status_code} for {url} was handled through compatibility mode "
"and returned the historical empty result. Pass strict_http=True to "
"raise MlbHttpError. This compatibility behavior may change in "
"version 1.0."
f"HTTP {status_code} for {url} was suppressed because "
"strict_http=False explicitly selected compatibility mode, so the "
"historical empty result was returned. Strict HTTP behavior is the "
"default in version 1.0. Remove strict_http=False or pass "
"strict_http=True to raise MlbHttpError."
),
MlbHttpCompatibilityWarning,
stacklevel=COMPATIBILITY_WARNING_STACKLEVEL,
stacklevel=_compatibility_warning_stacklevel(),
)


Expand Down
6 changes: 0 additions & 6 deletions tests/http_contract_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@ def assert_library_retry_policy(retry: Retry) -> None:
# Sentinel so helpers can omit strict_http and exercise the real constructor default.
_UNSET = object()

# Pending compatibility warning caller location via public Mlb endpoints (#285).
XFAIL_PENDING_WARNING_CALL_SITE = pytest.mark.xfail(
strict=True,
reason="Pending #285: compatibility warning must point to the public caller",
)


def adapter_for_api_version(mlb, api_version: str):
"""Return the internal MlbDataAdapter for v1 or v1.1."""
Expand Down
5 changes: 2 additions & 3 deletions tests/test_http_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
HTTP_REASON_BY_STATUS,
NOT_FOUND_STATUS,
SERVER_ERRORS,
XFAIL_PENDING_WARNING_CALL_SITE,
adapter_for_api_version,
standalone_adapter_for_version,
)
Expand Down Expand Up @@ -171,10 +170,11 @@ def test_compatibility_warning_message_contains_migration_guidance(status_code):
message = str(warning_info[0].message)
assert str(status_code) in message
assert SPORTS_URL in message
assert "strict_http=False" in message
assert "compatibility mode" in message
assert "default in version 1.0" in message
assert "strict_http=True" in message
assert "MlbHttpError" in message
assert "version 1.0" in message


def test_compatibility_warning_excludes_response_body():
Expand Down Expand Up @@ -490,7 +490,6 @@ def test_compatibility_warning_points_to_direct_adapter_caller_line():
assert warning.lineno == expected_lineno


@XFAIL_PENDING_WARNING_CALL_SITE
def test_compatibility_warning_points_to_public_mlb_endpoint_caller_line():
"""Public Mlb endpoint warnings must reference the application caller line."""
import inspect
Expand Down