@@ -357,28 +357,61 @@ class ScriptAuthorizer(Authorizer):
357
357
AUTHENTICATOR_CLASS = TrustedAuthenticator
358
358
359
359
def __init__ (
360
- self , authenticator , username , password , two_factor_callback = None
360
+ self ,
361
+ authenticator ,
362
+ username ,
363
+ password ,
364
+ two_factor_callback = None ,
361
365
):
362
366
"""Represent a single personal-use authorization to Reddit's API.
363
367
364
368
:param authenticator: An instance of :class:`TrustedAuthenticator`.
365
369
:param username: The Reddit username of one of the application's developers.
366
370
:param password: The password associated with ``username``.
367
- :param two_factor_callback: A function that returns OTPs (One-Time
368
- Passcodes), also known as 2FA auth codes. If this function is
369
- provided, prawcore will call it when authenticating.
371
+ :param two_factor_callback: (Optional) A function that returns OTPs (One-Time
372
+ Passcodes), also known as 2FA auth codes. If provided, this function should
373
+ return either a string of six digits or a 3-tuple of the form
374
+ ``(<OTP>, <DELAY>, <TRIES>)``, where ``<OTP>`` is a string of six
375
+ digits, ``<DELAY>`` is an integer that represents the number of seconds
376
+ to sleep between invalid authorization attempts, and ``<TRIES>`` is an
377
+ integer that represents the maximum number of authorization attempts to
378
+ make before an OAuthException is raised.
370
379
371
380
"""
372
- super (ScriptAuthorizer , self ).__init__ (authenticator )
381
+ super ().__init__ (authenticator )
373
382
self ._username = username
374
383
self ._password = password
375
384
self ._two_factor_callback = two_factor_callback
376
385
386
+ def _refresh_with_retries (self , count = 1 , delay = 0 , maxcount = 1 ):
387
+ if delay > 0 :
388
+ time .sleep (delay )
389
+ additional_kwargs = {}
390
+ otp = self ._two_factor_callback and self ._two_factor_callback ()
391
+ if otp :
392
+ if isinstance (otp , tuple ):
393
+ if otp [0 ]:
394
+ additional_kwargs ["otp" ] = otp [0 ]
395
+ else :
396
+ additional_kwargs ["otp" ] = otp
397
+ try :
398
+ self ._request_token (
399
+ grant_type = "password" ,
400
+ username = self ._username ,
401
+ password = self ._password ,
402
+ ** additional_kwargs ,
403
+ )
404
+ except OAuthException :
405
+ if otp and isinstance (otp , tuple ) and len (otp ) == 3 :
406
+ _ , delay , maxcount = otp
407
+ if count >= min (maxcount , 10 ):
408
+ raise
409
+ self ._refresh_with_retries (
410
+ count = count + 1 , delay = delay , maxcount = maxcount
411
+ )
412
+ else :
413
+ raise
414
+
377
415
def refresh (self ):
378
416
"""Obtain a new personal-use script type access token."""
379
- self ._request_token (
380
- grant_type = "password" ,
381
- username = self ._username ,
382
- password = self ._password ,
383
- otp = self ._two_factor_callback and self ._two_factor_callback (),
384
- )
417
+ self ._refresh_with_retries ()
0 commit comments