2

I noticed that in http.py a lot of the methods support num_retries which I believe is an implementation of exponential backoff (in case an API returns an error). Would like to use this rather than coding my own backoff algorithm each time I call an .execute() method. However, it doesn't seem to work with this code. Anyone have a handy idea on handling this backoff in a reusable way?

from apiclient.discovery import build

import google.auth
from google.auth.transport import requests
from google.auth import iam
from google.oauth2 import service_account

TOKEN_URI = 'https://accounts.google.com/o/oauth2/token'
ADMIN_DIRECTORY_SCOPES = [
    'https://www.googleapis.com/auth/admin.directory.user.readonly'
]
ADMIN_USER = 'my_gsuite_admin@domain.com'

def delegated_credentials(credentials, subject, scopes):
    try:
        admin_creds = credentials.with_subject(subject).with_scopes(scopes)
    except AttributeError:
        request = requests.Request()
        credentials.refresh(request)

        signer = iam.Signer(request, credentials, credentials.service_account_email)
        admin_creds = service_account.Credentials(
            signer, credentials.service_account_email, TOKEN_URI,
            scopes=scopes, subject=subject
        )
    except Exception:
        raise
    return admin_creds
print('Setting up auth')
default_credentials, _ = google.auth.default()
admin_creds = delegated_credentials(
    default_credentials, ADMIN_USER, ADMIN_DIRECTORY_SCOPES
)

directory_service = build('admin', 'directory_v1', credentials=admin_creds)

def get_ou(email):
    response = directory_service.users().get(
        userKey=email,
        fields='primaryEmail,orgUnitPath',
        num_retries=5
    ).execute()
    print('Directory.users.get: {}'.format(response))
    return response['orgUnitPath']

Traceback (most recent call last): File "", line 1, in File "/Users/mryerse001/Documents/GitHub/updateEmail_to_OU_Mappings/main.py", line 122, in get_pubsub_messages success = get_ou(email) File "/Users/mryerse001/Documents/GitHub/updateEmail_to_OU_Mappings/main.py", line 87, in get_ou num_retries=5 File "/Users/mryerse001/Documents/GitHub/updateEmail_to_OU_Mappings/venv/lib/python3.7/site-packages/googleapiclient/discovery.py", line 717, in method raise TypeError('Got an unexpected keyword argument "%s"' % name) TypeError: Got an unexpected keyword argument "num_retries"

Michael
  • 1,428
  • 3
  • 15
  • 34

1 Answers1

1

You need to pass the num_retries parameter to execute(), not to get().

From google-api-python-client code:

@util.positional(1)
def execute(self, http=None, num_retries=0):
    """Execute the request.

Args:
  http: httplib2.Http, an http object to be used in place of the
        one the HttpRequest request object was constructed with.
  num_retries: Integer, number of times to retry with randomized
        exponential backoff. If all retries fail, the raised HttpError
        represents the last request. If zero (default), we attempt the
        request only once.
sanjabhauz
  • 11
  • 2
  • You're right. Although I still don't like the retry functionality built into this client library because it will retry regardless of the error. For example let's say you're trying to edit a file that you don't have access to - one error is enough for that. I've started to use requests with auth'd sessions instead. – Michael Feb 11 '21 at 17:30
  • That shouldn't be the case, or at least it hasn't been for several years. I wonder if you're using a very old version of the library, or if Google is throwing unexpected errors? The library should retry 429s, >=500, and 403s with a "userRateLimitExceeded" or "rateLimitExceeded" reason. I'm curious specifically which errors are being retried that should not be? If you enable [logging](https://docs.python.org/3/howto/logging.html), you'll be able to see those retry attempts as warnings. – mary Mar 04 '21 at 16:09