diff --git a/pybaseball/retrosheet.py b/pybaseball/retrosheet.py index 391f55bb..7063900c 100644 --- a/pybaseball/retrosheet.py +++ b/pybaseball/retrosheet.py @@ -24,7 +24,7 @@ from pybaseball.utils import get_text_file from datetime import datetime from io import StringIO -from github import Github +from github import Auth, Github import os from getpass import getuser, getpass from github.GithubException import RateLimitExceededException @@ -131,7 +131,7 @@ def events(season, type='regular', export_dir='.'): "the valid types are: 'regular', 'post', and 'asg'.") try: - g = Github(GH_TOKEN) + g = Github(auth=Auth.Token(GH_TOKEN)) if GH_TOKEN else Github() repo = g.get_repo('chadwickbureau/retrosheet') season_folder = [f.path[f.path.rfind('/')+1:] for f in repo.get_contents(f'seasons/{season}')] season_events = [t for t in season_folder if t.endswith(file_extension)] @@ -156,7 +156,7 @@ def rosters(season): GH_TOKEN=os.getenv('GH_TOKEN', '') try: - g = Github(GH_TOKEN) + g = Github(auth=Auth.Token(GH_TOKEN)) if GH_TOKEN else Github() repo = g.get_repo('chadwickbureau/retrosheet') season_folder = [f.path[f.path.rfind('/')+1:] for f in repo.get_contents(f'seasons/{season}')] rosters = [t for t in season_folder if t.endswith('.ROS')] @@ -179,7 +179,7 @@ def _roster(team, season, checked = False): GH_TOKEN=os.getenv('GH_TOKEN', '') if not checked: - g = Github(GH_TOKEN) + g = Github(auth=Auth.Token(GH_TOKEN)) if GH_TOKEN else Github() try: repo = g.get_repo('chadwickbureau/retrosheet') season_folder = [f.path[f.path.rfind('/')+1:] for f in repo.get_contents(f'seasons/{season}')] @@ -213,7 +213,7 @@ def schedules(season): """ GH_TOKEN=os.getenv('GH_TOKEN', '') # validate input - g = Github(GH_TOKEN) + g = Github(auth=Auth.Token(GH_TOKEN)) if GH_TOKEN else Github() repo = g.get_repo('chadwickbureau/retrosheet') season_folder = [f.path[f.path.rfind('/')+1:] for f in repo.get_contents(f'seasons/{season}')] file_name = f'{season}schedule.csv' @@ -231,7 +231,7 @@ def season_game_logs(season): """ GH_TOKEN=os.getenv('GH_TOKEN', '') # validate input - g = Github(GH_TOKEN) + g = Github(auth=Auth.Token(GH_TOKEN)) if GH_TOKEN else Github() repo = g.get_repo('chadwickbureau/retrosheet') season_folder = [f.path[f.path.rfind('/')+1:] for f in repo.get_contents(f'seasons/{season}')] gamelog_file_name = f'GL{season}.TXT' diff --git a/tests/pybaseball/test_retrosheet.py b/tests/pybaseball/test_retrosheet.py new file mode 100644 index 00000000..3604ee78 --- /dev/null +++ b/tests/pybaseball/test_retrosheet.py @@ -0,0 +1,38 @@ +from unittest.mock import MagicMock + +import pytest + +import pybaseball.retrosheet as retrosheet + + +def test_events_uses_token_auth(monkeypatch: pytest.MonkeyPatch) -> None: + # Regression test for #455: when GH_TOKEN is set, the GitHub client must be + # built with the modern keyword auth API (Auth.Token) rather than the + # deprecated positional-token form. + monkeypatch.setenv('GH_TOKEN', 'dummy_token') + fake_github = MagicMock() + fake_github.return_value.get_repo.side_effect = RuntimeError("stop") + monkeypatch.setattr(retrosheet, 'Github', fake_github) + + with pytest.raises(RuntimeError): + retrosheet.events(2019) + + args, kwargs = fake_github.call_args + assert args == () + assert 'auth' in kwargs + + +def test_events_anonymous_without_token(monkeypatch: pytest.MonkeyPatch) -> None: + # Regression test for #455: with no GH_TOKEN, the client must be created + # anonymously (Github()) instead of passing an empty token string. + monkeypatch.delenv('GH_TOKEN', raising=False) + fake_github = MagicMock() + fake_github.return_value.get_repo.side_effect = RuntimeError("stop") + monkeypatch.setattr(retrosheet, 'Github', fake_github) + + with pytest.raises(RuntimeError): + retrosheet.events(2019) + + args, kwargs = fake_github.call_args + assert args == () + assert 'auth' not in kwargs