@@ -171,7 +171,7 @@ def fake_emit(*, repo, pr_number, head_sha, eval_hash, run_id):
171171 requester = requester ,
172172 allow_untrusted_author = allow_untrusted_author ,
173173 bot_login = bot_login ,
174- build_github = lambda _token : _CLIENT ,
174+ build_github = lambda _token , ** _kwargs : _CLIENT ,
175175 fetch = fake_fetch ,
176176 fetch_author = fake_fetch_author ,
177177 fingerprint = fake_fingerprint ,
@@ -618,7 +618,7 @@ def fake_dispatch(_client, number, head_sha, eval_hash, dispatch_ref):
618618 review .run (
619619 make_config (github_token = "t" ),
620620 max_dispatches = 1 ,
621- build_github = lambda _token : _CLIENT ,
621+ build_github = lambda _token , ** _kwargs : _CLIENT ,
622622 fetch = lambda _client : [_open_pr (n ) for n in numbers ],
623623 fingerprint = boom_fingerprint ,
624624 read_state = lambda _repo , _numbers : {},
@@ -856,7 +856,7 @@ def fake_dispatch(_client, number, head_sha, eval_hash, dispatch_ref):
856856 make_config (github_token = "t" ),
857857 pr = 7 ,
858858 force = True ,
859- build_github = lambda _token : _CLIENT ,
859+ build_github = lambda _token , ** _kwargs : _CLIENT ,
860860 fetch = lambda _client : [],
861861 fetch_author = lambda _client , _number : "albanD" ,
862862 fingerprint = boom_fingerprint ,
@@ -955,7 +955,7 @@ def fake_dispatch(_client, number, head_sha, eval_hash, dispatch_ref):
955955 ):
956956 review .run (
957957 make_config (github_token = "t" ),
958- build_github = lambda _token : _CLIENT ,
958+ build_github = lambda _token , ** _kwargs : _CLIENT ,
959959 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
960960 fingerprint = boom_fingerprint ,
961961 read_state = lambda _repo , _numbers : {2 : _state (2 , STATUS_LAND , _HASH_A , _NEW )},
@@ -989,7 +989,7 @@ def fake_dispatch(_client, number, head_sha, eval_hash, dispatch_ref):
989989 ):
990990 review .run (
991991 make_config (github_token = "t" ),
992- build_github = lambda _token : _CLIENT ,
992+ build_github = lambda _token , ** _kwargs : _CLIENT ,
993993 fetch = lambda _client : [_open_pr (3 ), _open_pr (1 ), _open_pr (2 )],
994994 fingerprint = boom_fingerprint ,
995995 read_state = lambda _repo , _numbers : {},
@@ -1020,7 +1020,7 @@ def boom_dispatch(_client, number, _head_sha, _eval_hash, _ref):
10201020 ):
10211021 review .run (
10221022 make_config (github_token = "t" ),
1023- build_github = lambda _token : _CLIENT ,
1023+ build_github = lambda _token , ** _kwargs : _CLIENT ,
10241024 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
10251025 fingerprint = lambda _client , number , _authorized , _skip : (f"headsha{ number } " , _HASH_A ),
10261026 read_state = lambda _repo , _numbers : {},
@@ -1049,7 +1049,7 @@ def boom_dispatch(_client, number, _head_sha, _eval_hash, _ref):
10491049 ):
10501050 review .run (
10511051 make_config (github_token = "t" ),
1052- build_github = lambda _token : _CLIENT ,
1052+ build_github = lambda _token , ** _kwargs : _CLIENT ,
10531053 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
10541054 fingerprint = lambda _client , number , _authorized , _skip : (f"headsha{ number } " , _HASH_A ),
10551055 read_state = lambda _repo , _numbers : {},
@@ -1074,7 +1074,7 @@ def timeout_dispatch(_client, number, _head_sha, _eval_hash, _ref):
10741074 with pytest .raises (IterationTimeout ):
10751075 review .run (
10761076 make_config (github_token = "t" ),
1077- build_github = lambda _token : _CLIENT ,
1077+ build_github = lambda _token , ** _kwargs : _CLIENT ,
10781078 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
10791079 fingerprint = lambda _client , number , _authorized , _skip : (f"headsha{ number } " , _HASH_A ),
10801080 read_state = lambda _repo , _numbers : {},
@@ -1105,7 +1105,7 @@ def boom_dispatch(_client, number, _head_sha, _eval_hash, _ref):
11051105 with caplog .at_level (logging .ERROR , logger = "greenlight" ), pytest .raises (RuntimeError ) as excinfo :
11061106 review .run (
11071107 make_config (github_token = "t" ),
1108- build_github = lambda _token : _CLIENT ,
1108+ build_github = lambda _token , ** _kwargs : _CLIENT ,
11091109 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
11101110 fingerprint = boom_fingerprint ,
11111111 read_state = lambda _repo , _numbers : {},
@@ -1153,7 +1153,7 @@ def fake_dispatch(_client, number, head_sha, eval_hash, dispatch_ref):
11531153
11541154 review .run (
11551155 make_config (github_token = "t" ),
1156- build_github = lambda _token : _CLIENT ,
1156+ build_github = lambda _token , ** _kwargs : _CLIENT ,
11571157 fetch = lambda _client : [_open_pr (n ) for n in numbers ],
11581158 fingerprint = barrier_fingerprint ,
11591159 read_state = lambda _repo , _numbers : {},
@@ -1176,7 +1176,7 @@ def test_worker_clients_are_isolated_and_exclude_main_client(make_config):
11761176 barrier = threading .Barrier (k , timeout = 5 )
11771177 built : list [object ] = []
11781178
1179- def factory (_token ):
1179+ def factory (_token , ** _kwargs ):
11801180 client = object ()
11811181 built .append (client )
11821182 return cast ("Github" , client )
@@ -1218,16 +1218,17 @@ def fake_dispatch(_client, number, head_sha, eval_hash, dispatch_ref):
12181218
12191219def test_run_closes_main_and_worker_clients (make_config ):
12201220 class _Closeable :
1221- def __init__ (self ) -> None :
1221+ def __init__ (self , seconds_between_requests : float | None ) -> None :
12221222 self .closed = 0
1223+ self .seconds_between_requests = seconds_between_requests
12231224
12241225 def close (self ) -> None :
12251226 self .closed += 1
12261227
12271228 built : list [_Closeable ] = []
12281229
1229- def factory (_token ):
1230- client = _Closeable ()
1230+ def factory (_token , * , seconds_between_requests = None ):
1231+ client = _Closeable (seconds_between_requests )
12311232 built .append (client )
12321233 return cast ("Github" , client )
12331234
@@ -1246,6 +1247,16 @@ def factory(_token):
12461247 # each is closed exactly once as the scan unwinds -- the connection pools are not leaked.
12471248 assert len (built ) == 3
12481249 assert all (client .closed == 1 for client in built )
1250+ # The main client (built first) keeps PyGithub's default pacing -- no kwarg passed -- while the
1251+ # two fingerprint worker clients are throttled to bound the fan-out's aggregate request rate.
1252+ assert built [0 ].seconds_between_requests is None
1253+ assert [c .seconds_between_requests for c in built [1 :]] == [review ._FINGERPRINT_SECONDS_BETWEEN_REQUESTS ] * 2
1254+
1255+
1256+ def test_fingerprint_throttle_stays_under_burst_limit ():
1257+ # Aggregate fan-out rate is workers / seconds-between-requests; keep it <= 8 req/s to stay
1258+ # under GitHub's secondary (burst-rate) limit.
1259+ assert review ._FINGERPRINT_WORKERS / review ._FINGERPRINT_SECONDS_BETWEEN_REQUESTS <= 8
12491260
12501261
12511262def test_close_client_swallows_close_errors (caplog ):
@@ -1302,7 +1313,7 @@ def fake_dispatch(_client, number, _head_sha, _eval_hash, _ref):
13021313 with pytest .raises (RuntimeError , match = r"1 PR\(s\) failed during scan: \[1\]" ):
13031314 review .run (
13041315 make_config (github_token = "t" ),
1305- build_github = lambda _token : _CLIENT ,
1316+ build_github = lambda _token , ** _kwargs : _CLIENT ,
13061317 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
13071318 fingerprint = fingerprint ,
13081319 read_state = lambda _repo , _numbers : {},
@@ -1337,7 +1348,7 @@ def fake_dispatch(_client, number, _head_sha, _eval_hash, _ref):
13371348 with pytest .raises (RuntimeError , match = r"1 PR\(s\) failed during scan: \[2\]" ):
13381349 review .run (
13391350 make_config (github_token = "t" ),
1340- build_github = lambda _token : _CLIENT ,
1351+ build_github = lambda _token , ** _kwargs : _CLIENT ,
13411352 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
13421353 fingerprint = fingerprint ,
13431354 read_state = lambda _repo , _numbers : {},
@@ -1373,7 +1384,7 @@ def fake_dispatch(_client, number, _head_sha, _eval_hash, _ref):
13731384 review .run (
13741385 make_config (github_token = "t" ),
13751386 max_dispatches = 5 ,
1376- build_github = lambda _token : _CLIENT ,
1387+ build_github = lambda _token , ** _kwargs : _CLIENT ,
13771388 fetch = lambda _client : [_open_pr (1 ), _open_pr (2 ), _open_pr (3 )],
13781389 fingerprint = fingerprint ,
13791390 read_state = lambda _repo , _numbers : {},
@@ -1398,7 +1409,7 @@ def boom_fetch(_client):
13981409 with pytest .raises (RuntimeError , match = "fetch boom" ):
13991410 review .run (
14001411 make_config (github_token = "t" ),
1401- build_github = lambda _token : cast ("Github" , client ),
1412+ build_github = lambda _token , ** _kwargs : cast ("Github" , client ),
14021413 fetch = boom_fetch ,
14031414 fingerprint = lambda _client , number , _authorized , _skip : (f"headsha{ number } " , _HASH_A ),
14041415 read_state = lambda _repo , _numbers : {},
@@ -1471,7 +1482,7 @@ def boom_resolve() -> frozenset[str]:
14711482 with pytest .raises (RuntimeError , match = "merge_rules unreachable" ):
14721483 review .run (
14731484 make_config (github_token = "t" ),
1474- build_github = lambda _token : _CLIENT ,
1485+ build_github = lambda _token , ** _kwargs : _CLIENT ,
14751486 fetch = lambda _client : [_open_pr (1 )],
14761487 fingerprint = lambda _client , number , _authorized , _skip : (f"headsha{ number } " , _HASH_A ),
14771488 read_state = lambda _repo , _numbers : {},
0 commit comments