Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add type hints to client #302

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions google/cloud/_helpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@
This module is not part of the public API surface.
"""

from __future__ import absolute_import

import calendar
import datetime
import http.client
import os
import re
from threading import local as Local
from typing import Union
from typing import Optional, Union

import google.auth
import google.auth.transport.requests
Expand Down Expand Up @@ -136,7 +134,7 @@ def _ensure_tuple_or_list(arg_name, tuple_or_list):
return list(tuple_or_list)


def _determine_default_project(project=None):
def _determine_default_project(project: Optional[str] = None):
"""Determine default project ID explicitly or implicitly as fall-back.

See :func:`google.auth.default` for details on how the default project
Expand Down
58 changes: 32 additions & 26 deletions google/cloud/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
import json
import os
from pickle import PicklingError
from typing import Tuple
from typing import Union
from typing import Any, Dict, Optional, Self, Tuple, Union

import requests

import google.api_core.client_options
from google.api_core.client_options import ClientOptions
import google.api_core.exceptions
import google.auth
from google.auth import environment_vars
Expand All @@ -31,12 +33,6 @@
from google.oauth2 import service_account


_GOOGLE_AUTH_CREDENTIALS_HELP = (
"This library only supports credentials from google-auth-library-python. "
"See https://google-auth.readthedocs.io/en/latest/ "
"for help on authentication with this library."
)

# Default timeout for auth requests.
_CREDENTIALS_REFRESH_TIMEOUT = 300

Expand All @@ -52,7 +48,7 @@ class _ClientFactoryMixin(object):
_SET_PROJECT = False

@classmethod
def from_service_account_info(cls, info, *args, **kwargs):
def from_service_account_info(cls, info, *args, **kwargs) -> Self:
"""Factory to retrieve JSON credentials while creating client.

:type info: dict
Expand Down Expand Up @@ -82,7 +78,7 @@ def from_service_account_info(cls, info, *args, **kwargs):
return cls(*args, **kwargs)

@classmethod
def from_service_account_json(cls, json_credentials_path, *args, **kwargs):
def from_service_account_json(cls, json_credentials_path: str, *args, **kwargs):
"""Factory to retrieve JSON credentials while creating client.

:type json_credentials_path: str
Expand Down Expand Up @@ -150,22 +146,23 @@ class Client(_ClientFactoryMixin):
Needs to be set by subclasses.
"""

def __init__(self, credentials=None, _http=None, client_options=None):
def __init__(
self,
credentials: Optional[google.auth.credentials.Credentials] = None,
_http: Optional[requests.Session] = None,
client_options: Union[ClientOptions, Dict[str, Any], None] = None,
):
if isinstance(client_options, dict):
client_options = google.api_core.client_options.from_dict(client_options)
if client_options is None:
client_options = google.api_core.client_options.ClientOptions()
client_options = ClientOptions()
assert isinstance(client_options, ClientOptions)

if credentials and client_options.credentials_file:
raise google.api_core.exceptions.DuplicateCredentialArgs(
"'credentials' and 'client_options.credentials_file' are mutually exclusive."
)

if credentials and not isinstance(
credentials, google.auth.credentials.Credentials
):
raise ValueError(_GOOGLE_AUTH_CREDENTIALS_HELP)

scopes = client_options.scopes or self.SCOPE

# if no http is provided, credentials must exist
Expand All @@ -177,11 +174,16 @@ def __init__(self, credentials=None, _http=None, client_options=None):
else:
credentials, _ = google.auth.default(scopes=scopes)

assert isinstance(credentials, google.auth.credentials.Credentials)
assert scopes is not None
self._credentials = google.auth.credentials.with_scopes_if_required(
credentials, scopes=scopes
)

if client_options.quota_project_id:
assert isinstance(
self._credentials, google.auth.credentials.CredentialsWithQuotaProject
)
self._credentials = self._credentials.with_quota_project(
client_options.quota_project_id
)
Expand Down Expand Up @@ -248,7 +250,11 @@ class _ClientProjectMixin(object):
if the project value is invalid.
"""

def __init__(self, project=None, credentials=None):
def __init__(
self,
project: Optional[str] = None,
credentials: Optional[google.auth.credentials.Credentials] = None,
):
# This test duplicates the one from `google.auth.default`, but earlier,
# for backward compatibility: we want the environment variable to
# override any project set on the credentials. See:
Expand All @@ -273,16 +279,10 @@ def __init__(self, project=None, credentials=None):
"determined from the environment."
)

if isinstance(project, bytes):
project = project.decode("utf-8")

if not isinstance(project, str):
raise ValueError("Project must be a string.")

self.project = project

@staticmethod
def _determine_default(project):
def _determine_default(project: Optional[str]) -> Optional[str]:
"""Helper: use default project detection."""
return _determine_default_project(project)

Expand Down Expand Up @@ -316,7 +316,13 @@ class ClientWithProject(Client, _ClientProjectMixin):

_SET_PROJECT = True # Used by from_service_account_json()

def __init__(self, project=None, credentials=None, client_options=None, _http=None):
def __init__(
self,
project: Optional[str] = None,
credentials: Optional[google.auth.credentials.Credentials] = None,
client_options: Optional[ClientOptions] = None,
_http: Optional[requests.Session] = None,
):
_ClientProjectMixin.__init__(self, project=project, credentials=credentials)
Client.__init__(
self, credentials=credentials, client_options=client_options, _http=_http
Expand Down