Skip to content

Commit

Permalink
Merge pull request #1075 from jlumbroso/master
Browse files Browse the repository at this point in the history
Add `client_factory` parameter to `auth` methods
  • Loading branch information
lavigne958 committed Jul 3, 2022
2 parents d5a5e61 + 133e138 commit 50fc9b3
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 25 deletions.
2 changes: 2 additions & 0 deletions HISTORY.rst
@@ -1,6 +1,8 @@
Release History
===============

* Add `client_factory` param to `auth` methods by @jlumbroso in https://github.com/burnash/gspread/pull/1075

5.4.0 (2022-06-01)
------------------
* fix typo by @joswlv in https://github.com/burnash/gspread/pull/1031
Expand Down
23 changes: 8 additions & 15 deletions gspread/__init__.py
Expand Up @@ -13,9 +13,15 @@
__author__ = "Anton Burnashev"


from .auth import oauth, oauth_from_dict, service_account, service_account_from_dict
from .auth import (
authorize,
oauth,
oauth_from_dict,
service_account,
service_account_from_dict,
)
from .cell import Cell
from .client import Client
from .client import BackoffClient, Client, ClientFactory
from .exceptions import (
CellNotFound,
GSpreadException,
Expand All @@ -26,16 +32,3 @@
)
from .spreadsheet import Spreadsheet
from .worksheet import Worksheet


def authorize(credentials, client_class=Client):
"""Login to Google API using OAuth2 credentials.
This is a shortcut function which
instantiates `client_class`.
By default :class:`gspread.Client` is used.
:returns: `client_class` instance.
"""

client = client_class(auth=credentials)
return client
56 changes: 46 additions & 10 deletions gspread/auth.py
Expand Up @@ -49,6 +49,20 @@ def get_config_dir(config_dir_name="gspread", os_is_windows=os.name == "nt"):
DEFAULT_SERVICE_ACCOUNT_FILENAME = DEFAULT_CONFIG_DIR / "service_account.json"


def authorize(credentials, client_factory=Client):
"""Login to Google API using OAuth2 credentials.
This is a shortcut/helper function which
instantiates a client using `client_factory`.
By default :class:`gspread.Client` is used (but could also use
:class:`gspread.BackoffClient` to avoid rate limiting).
:returns: An instance of the class produced by `client_factory`.
:rtype: :class:`gspread.client.Client`
"""

return client_factory(auth=credentials)


def local_server_flow(client_config, scopes, port=0):
"""Run an OAuth flow using a local server strategy.
Expand Down Expand Up @@ -105,6 +119,7 @@ def oauth(
flow=local_server_flow,
credentials_filename=DEFAULT_CREDENTIALS_FILENAME,
authorized_user_filename=DEFAULT_AUTHORIZED_USER_FILENAME,
client_factory=Client,
):
r"""Authenticate with OAuth Client ID.
Expand Down Expand Up @@ -162,8 +177,12 @@ def oauth(
* `%APPDATA%\gspread\authorized_user.json` on Windows
* `~/.config/gspread/authorized_user.json` everywhere else
:type client_factory: :class:`gspread.ClientFactory`
:param client_factory: A factory function that returns a client class.
Defaults to :class:`gspread.Client` (but could also use
:class:`gspread.BackoffClient` to avoid rate limiting)
:rtype: :class:`gspread.Client`
:rtype: :class:`gspread.client.Client`
"""
authorized_user_filename = Path(authorized_user_filename)
creds = load_credentials(filename=authorized_user_filename)
Expand All @@ -174,14 +193,15 @@ def oauth(
creds = flow(client_config=client_config, scopes=scopes)
store_credentials(creds, filename=authorized_user_filename)

return Client(auth=creds)
return client_factory(auth=creds)


def oauth_from_dict(
credentials=None,
authorized_user_info=None,
scopes=DEFAULT_SCOPES,
flow=local_server_flow,
client_factory=Client,
):
r"""Authenticate with OAuth Client ID.
Expand Down Expand Up @@ -237,8 +257,12 @@ def oauth_from_dict(
:param list scopes: The scopes used to obtain authorization.
:param function flow: OAuth flow to use for authentication.
Defaults to :meth:`~gspread.auth.local_server_flow`
:type client_factory: :class:`gspread.ClientFactory`
:param client_factory: A factory function that returns a client class.
Defaults to :class:`gspread.Client` (but could also use
:class:`gspread.BackoffClient` to avoid rate limiting)
:rtype: tuple(:class:`gspread.Client` , str)
:rtype: (`gspread.client.Client`, str)
"""

creds = None
Expand All @@ -248,15 +272,19 @@ def oauth_from_dict(
if not creds:
creds = flow(client_config=credentials, scopes=scopes)

client = Client(auth=creds)
client = client_factory(auth=creds)

# must return the creds to the user
# must strip the token an use the dedicated method from Credentials
# to return a dict "safe to store".
return (client, creds.to_json("token"))


def service_account(filename=DEFAULT_SERVICE_ACCOUNT_FILENAME, scopes=DEFAULT_SCOPES):
def service_account(
filename=DEFAULT_SERVICE_ACCOUNT_FILENAME,
scopes=DEFAULT_SCOPES,
client_factory=Client,
):
"""Authenticate using a service account.
``scopes`` parameter defaults to read/write scope available in
Expand All @@ -274,14 +302,18 @@ def service_account(filename=DEFAULT_SERVICE_ACCOUNT_FILENAME, scopes=DEFAULT_SC
:param str filename: The path to the service account json file.
:param list scopes: The scopes used to obtain authorization.
:type client_factory: :class:`gspread.ClientFactory`
:param client_factory: A factory function that returns a client class.
Defaults to :class:`gspread.Client` (but could also use
:class:`gspread.BackoffClient` to avoid rate limiting)
:rtype: :class:`gspread.Client`
:rtype: :class:`gspread.client.Client`
"""
creds = ServiceAccountCredentials.from_service_account_file(filename, scopes=scopes)
return Client(auth=creds)
return client_factory(auth=creds)


def service_account_from_dict(info, scopes=DEFAULT_SCOPES):
def service_account_from_dict(info, scopes=DEFAULT_SCOPES, client_factory=Client):
"""Authenticate using a service account (json).
``scopes`` parameter defaults to read/write scope available in
Expand All @@ -299,11 +331,15 @@ def service_account_from_dict(info, scopes=DEFAULT_SCOPES):
:param info (Mapping[str, str]): The service account info in Google format
:param list scopes: The scopes used to obtain authorization.
:type client_factory: :class:`gspread.ClientFactory`
:param client_factory: A factory function that returns a client class.
Defaults to :class:`gspread.Client` (but could also use
:class:`gspread.BackoffClient` to avoid rate limiting)
:rtype: :class:`gspread.Client`
:rtype: :class:`gspread.client.Client`
"""
creds = ServiceAccountCredentials.from_service_account_info(
info=info,
scopes=scopes,
)
return Client(auth=creds)
return client_factory(auth=creds)
9 changes: 9 additions & 0 deletions gspread/client.py
Expand Up @@ -8,6 +8,7 @@
"""

from http import HTTPStatus
from typing import Type

from google.auth.transport.requests import AuthorizedSession

Expand Down Expand Up @@ -534,6 +535,11 @@ class BackoffClient(Client):
This Client is not production ready yet.
Use it at your own risk !
.. note::
To use with the `auth` module, make sure to pass this backoff
client factory using the ``client_factory`` parameter of the
method used.
.. note::
Currently known issues are:
Expand Down Expand Up @@ -581,3 +587,6 @@ def request(self, *args, **kwargs):

# failed too many times, raise APIEerror
raise err


ClientFactory = Type[Client]

0 comments on commit 50fc9b3

Please sign in to comment.