Skip to content

Improvements and tweaks#649

Open
sreckoskocilic wants to merge 6 commits intoswar:masterfrom
sreckoskocilic:minor_performance_improvements
Open

Improvements and tweaks#649
sreckoskocilic wants to merge 6 commits intoswar:masterfrom
sreckoskocilic:minor_performance_improvements

Conversation

@sreckoskocilic
Copy link
Copy Markdown

@sreckoskocilic sreckoskocilic commented Mar 3, 2026

I have analyzed my demo app along with external libraries used. It is using nba_api lib to keep track on recent games and player stats. Probably it would be good to make a revision on the fixes provided in this PR, especially the one reducing redundant json.loads by implementing dict_cache. Notable is reducing get_parameters() calls of get_dict() from 3 to 1 and find_players() compiles re only one per search now.

Changes summary

  • HTTP layer (library/http.py): NBAResponse now caches parsed JSON so repeated .get_dict() / .get_json()
    calls don't re-parse. Fixed a bug where passing a referer would mutate the shared class-level
    STATS_HEADERS dict permanently. Debug cache path was silently skipping raise_exception_on_error checks —
    fixed by setting status_code=200 on cache hits. Added type annotations to NBAResponse, NBAHTTP, and
    send_api_request().
  • Stats response (stats/library/http.py): The resultSets/resultSet lookup was copy-pasted in 3 places —
    pulled it into _get_legacy_results(). A bare except KeyError: pass was eating errors from v3 parsers, so
    parser bugs would just silently return empty data. get_data_sets() would crash with a KeyError if neither
    resultSets nor resultSet existed in the response.
  • Player/team lookups (stats/static/players.py, teams.py): ID and abbreviation lookups are now O(1) via
    pre-built dicts instead of scanning the full list. Regex compilation is LRU-cached. Name searches strip
    accents, so Jokić and Jokic both match. Added type hints to all public functions.
  • Endpoint base class (stats/endpoints/_base.py): data_sets had a mutable class default ([]) shared across
    instances — changed to None. Replaced if "key" in dict checks with .get() and defaults.
    get_available_data() was returning a dict_keys view instead of a list. Missing pandas now raises
    ImportError instead of a bare Exception.
  • V3 parsers (stats/endpoints/_parsers/init.py): get_parser_for_endpoint() used to raise KeyError for
    unregistered endpoints. Now returns None, which means actual parser bugs (also KeyErrors) stop getting
    silently caught by the same handler.
  • Live API (live/nba/): Type hints on the endpoint base class. Renamed STATS_HEADERS to LIVE_HEADERS in
    the live HTTP module — it was pointing at CDN headers, not stats headers.
  • Makefile: make format and make lint were calling isort, black, flake8, and pylint, none of which are
    installed. Replaced with ruff. Added make lint-fix.
  • Tests: 15 tests covering the headers mutation fix, parser lookup returning None, the legacy results
    helper, and the missing key guards.

Test plan

  • All 537 unit tests pass (522 existing + 15 new)
  • Ruff lint + format clean
  • Verify make check, make lint-fix work

sreckoskocilic and others added 4 commits March 2, 2026 09:00
- refactor get_parameters update {k:v} anti-pattern
- improve find_player_by_id with single dict get() call
- find_players search speedup
…mmit

- status check in raise_exception_on_error raises on HTTP 4xx/5xx before checking json validity (previously a 429 or 500 would silently pass)
…ecks)

- added strip_accents for find_teams
- ImportError should not be silenced as it could indicate a broken module (parser with syntax error)
@sreckoskocilic sreckoskocilic requested a review from swar as a code owner March 3, 2026 08:10
endpoint_parser = get_parser_for_endpoint(self._endpoint, raw_data)
for name, dataset in endpoint_parser.get_data_sets().items():
data[name] = self._build_rows(dataset["headers"], dataset["data"])
except (KeyError, ImportError):
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the import error here? To be honest, I'm not sure why it's even catching it here in the first place

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the import of get_parser_for_endpoint fails, that's a critical issue that should raise an exception, not silently
continue with an empty result

@swar
Copy link
Copy Markdown
Owner

swar commented Mar 10, 2026

No major complaints from my initial pass. I need to do a deeper dive here and then push this one through. All the changes you suggested make sense

if raise_exception_on_error and not data.valid_json():
raise Exception("InvalidResponse: Response is not in a valid JSON format.")
if raise_exception_on_error:
if status_code is not None and status_code >= 400:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the additional breakout of the exception?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code now checks the HTTP status code first (any >= 400) raising an Exception with the status code, before
validating JSON. This ensures HTTP errors are properly caught.

…ad of mutating class-level

STATS_HEADERS)
- fix debug cache bypassing raise_exception_on_error (set status_code=200 when loading from file)
- fix silent KeyError swallowing in v3 parser dispatch (get_parser_for_endpoint returns None instead of
raising)
- fix get_data_sets KeyError when neither resultSets nor resultSet present (use .get() with guard)
- fix Makefile referencing dead tools (isort/black/flake8/pylint → ruff)
- fix live http.py using wrong variable name STATS_HEADERS → LIVE_HEADERS
- add type hints to public API (players.py, teams.py, http.py, live _base.py)
- extract _get_legacy_results helper to DRY repeated resultSets/resultSet lookup
- improve _base.py: mutable default → None, .get() patterns, ImportError, get_available_data returns list
- add 15 regression tests for bug fixes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants