Skip to content

Commit 95af6c7

Browse files
authored
Merge pull request #174 from kuzmoyev/dev
Support new credentials file names
2 parents 04dc309 + 2430125 commit 95af6c7

File tree

8 files changed

+87
-30
lines changed

8 files changed

+87
-30
lines changed

docs/source/authentication.rst

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ There are several ways to authenticate in ``GoogleCalendar``.
88
Credentials file
99
----------------
1010

11-
If you have a ``credentials.json`` file (see :ref:`getting_started`), ``GoogleCalendar`` will read all the needed data
12-
to generate the token and refresh-token from it.
11+
If you have a ``credentials.json`` (``client_secret_*.json``) file (see :ref:`getting_started`), ``GoogleCalendar``
12+
will read all the needed data to generate the token and refresh-token from it.
1313

14-
To read ``credentials.json`` from the default path (``~/.credentials/credentials.json``) use:
14+
To read ``credentials.json`` (``client_secret_*.json``) from the default directory (``~/.credentials``) use:
1515

1616
.. code-block:: python
1717
1818
gc = GoogleCalendar()
1919
2020
In this case, if ``~/.credentials/token.pickle`` file exists, it will read it and refresh only if needed. If
2121
``token.pickle`` does not exist, it will be created during authentication flow and saved alongside with
22-
``credentials.json`` in ``~/.credentials/token.pickle``.
22+
``credentials.json`` (``client_secret_*.json``) in ``~/.credentials/token.pickle``.
2323

2424
To **avoid saving** the token use:
2525

@@ -29,15 +29,21 @@ To **avoid saving** the token use:
2929
3030
After token is generated during authentication flow, it can be accessed in ``gc.credentials`` field.
3131

32-
To specify ``credentials.json`` file path use ``credentials_path`` parameter:
32+
To specify ``credentials.json`` (``client_secret_*.json``) file path use ``credentials_path`` parameter:
3333

3434
.. code-block:: python
3535
3636
gc = GoogleCalendar(credentials_path='path/to/credentials.json')
3737
38+
or
39+
40+
.. code-block:: python
41+
42+
gc = GoogleCalendar(credentials_path='path/to/client_secret_273833015691-qwerty.apps.googleusercontent.com.json')
43+
3844
Similarly, if ``token.pickle`` file exists in the same folder (``path/to/``), it will be used and refreshed only if
39-
needed. If it doesn't exist, it will be generated and stored alongside the ``credentials.json`` (in
40-
``path/to/token.pickle``).
45+
needed. If it doesn't exist, it will be generated and stored alongside the ``credentials.json`` (``client_secret_*.json``)
46+
(in ``path/to/token.pickle``).
4147

4248
To specify different path for the pickled token file use ``token_path`` parameter:
4349

docs/source/change_log.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@
33
Change log
44
==========
55

6+
v2.2.0
7+
~~~~~~
8+
9+
API
10+
---
11+
* Adds support for new credentials file names (i.e. client_secret_*.json)
12+
13+
Core
14+
----
15+
* None
16+
17+
Backward compatibility
18+
----------------------
19+
* Full compatibility
20+
621

722
v2.1.0
823
~~~~~~

docs/source/getting_started.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ Now you need to get your API credentials:
3333
.. note:: You will need to enable the "Google Calendar API" for your project.
3434

3535
2. `Configure the OAuth consent screen`_
36-
3. `Create a OAuth client ID credential`_ and download the ``credentials.json`` file
37-
4. Put downloaded ``credentials.json`` file into ``~/.credentials/`` directory
36+
3. `Create a OAuth client ID credential`_ and download the ``credentials.json`` (``client_secret_*.json``) file
37+
4. Put downloaded ``credentials.json`` (``client_secret_*.json``) file into ``~/.credentials/`` directory
3838

3939

4040
.. _`Create a new Google Cloud Platform (GCP) project`: https://developers.google.com/workspace/guides/create-project
@@ -44,15 +44,15 @@ Now you need to get your API credentials:
4444

4545
See more options in :ref:`authentication`.
4646

47-
.. note:: You can put ``credentials.json`` file anywhere you want and specify
47+
.. note:: You can put ``credentials.json`` (``client_secret_*.json``) file anywhere you want and specify
4848
the path to it in your code afterwords. But remember not to share it (e.g. add it
4949
to ``.gitignore``) as it is your private credentials.
5050

5151
.. note::
5252
| On the first run, your application will prompt you to the default browser
5353
to get permissions from you to use your calendar. This will create
5454
``token.pickle`` file in the same folder (unless specified otherwise) as your
55-
``credentials.json``. So don't forget to also add it to ``.gitignore`` if
55+
``credentials.json`` (``client_secret_*.json``). So don't forget to also add it to ``.gitignore`` if
5656
it is in a GIT repository.
5757
| If you don't want to save it in ``.pickle`` file, you can use ``save_token=False``
5858
when initializing the ``GoogleCalendar``.

gcsa/_services/authentication.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pickle
22
import os.path
3+
import glob
34
from typing import List
45

56
from googleapiclient import discovery
@@ -32,10 +33,11 @@ def __init__(
3233
Credentials with token and refresh token.
3334
If specified, ``credentials_path``, ``token_path``, and ``save_token`` are ignored.
3435
If not specified, credentials are retrieved from "token.pickle" file (specified in ``token_path`` or
35-
default path) or with authentication flow using secret from "credentials.json" (specified in
36-
``credentials_path`` or default path)
36+
default path) or with authentication flow using secret from "credentials.json" ("client_secret_*.json")
37+
(specified in ``credentials_path`` or default path)
3738
:param credentials_path:
38-
Path to "credentials.json" file. Default: ~/.credentials
39+
Path to "credentials.json" ("client_secret_*.json") file.
40+
Default: ~/.credentials/credentials.json or ~/.credentials/client_secret*.json
3941
:param token_path:
4042
Existing path to load the token from, or path to save the token after initial authentication flow.
4143
Default: "token.pickle" in the same directory as the credentials_path
@@ -109,12 +111,28 @@ def _get_credentials(
109111

110112
@staticmethod
111113
def _get_default_credentials_path() -> str:
112-
"""Checks if ".credentials" folder in home directory exists. If not, creates it.
113-
:return: expanded path to .credentials folder
114+
"""Checks if `.credentials` folder in home directory exists and contains `credentials.json` or
115+
`client_secret*.json` file.
116+
117+
:raises ValueError: if `.credentials` folder does not exist, none of `credentials.json` or `client_secret*.json`
118+
files do not exist, or there are multiple `client_secret*.json` files.
119+
:return: expanded path to `credentials.json` or `client_secret*.json` file
114120
"""
115121
home_dir = os.path.expanduser('~')
116122
credential_dir = os.path.join(home_dir, '.credentials')
117123
if not os.path.exists(credential_dir):
118-
os.makedirs(credential_dir)
124+
raise FileNotFoundError(f'Default credentials directory "{credential_dir}" does not exist.')
119125
credential_path = os.path.join(credential_dir, 'credentials.json')
120-
return credential_path
126+
if os.path.exists(credential_path):
127+
return credential_path
128+
else:
129+
credentials_files = glob.glob(credential_dir + '/client_secret*.json')
130+
if len(credentials_files) > 1:
131+
raise ValueError(f"Multiple credential files found in {credential_dir}.\n"
132+
f"Try specifying the credentials file, e.x.:\n"
133+
f"GoogleCalendar(credentials_path='{credentials_files[0]}')")
134+
elif not credentials_files:
135+
raise FileNotFoundError(f'Credentials file (credentials.json or client_secret*.json)'
136+
f'not found in the default path: "{credential_dir}".')
137+
else:
138+
return credentials_files[0]

gcsa/event.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ def __repr__(self):
278278

279279
def __lt__(self, other):
280280
def ensure_datetime(d, timezone):
281-
if type(d) == date:
281+
if type(d) is date:
282282
return ensure_localisation(datetime(year=d.year, month=d.month, day=d.day), timezone)
283283
else:
284284
return d

gcsa/google_calendar.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ def __init__(
4747
Credentials with token and refresh token.
4848
If specified, ``credentials_path``, ``token_path``, and ``save_token`` are ignored.
4949
If not specified, credentials are retrieved from "token.pickle" file (specified in ``token_path`` or
50-
default path) or with authentication flow using secret from "credentials.json" (specified in
51-
``credentials_path`` or default path)
50+
default path) or with authentication flow using secret from "credentials.json" ("client_secret_*.json")
51+
(specified in ``credentials_path`` or default path)
5252
:param credentials_path:
53-
Path to "credentials.json" file. Default: ~/.credentials
53+
Path to "credentials.json" ("client_secret_*.json") file.
54+
Default: ~/.credentials/credentials.json or ~/.credentials/client_secret*.json
5455
:param token_path:
5556
Existing path to load the token from, or path to save the token after initial authentication flow.
5657
Default: "token.pickle" in the same directory as the credentials_path

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def run(self):
1616

1717
here = os.path.abspath(os.path.dirname(__file__))
1818

19-
VERSION = '2.1.0'
19+
VERSION = '2.2.0'
2020

2121

2222
class UploadCommand(Command):

tests/google_calendar_tests/test_authentication.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class TestGoogleCalendarCredentials(TestCase):
1313
def setUp(self):
1414
self.setUpPyfakefs()
1515

16-
self.credentials_dir = '/.credentials'
16+
self.credentials_dir = path.join(path.expanduser('~'), '.credentials')
1717
self.credentials_path = path.join(self.credentials_dir, 'credentials.json')
1818
self.fs.create_dir(self.credentials_dir)
1919
self.fs.create_file(self.credentials_path)
@@ -53,20 +53,37 @@ def test_with_given_credentials_expired(self):
5353
self.assertTrue(gc.credentials.valid)
5454
self.assertFalse(gc.credentials.expired)
5555

56-
def test_get_default_credentials_path_exist(self):
57-
self.fs.create_dir(path.join(path.expanduser('~'), '.credentials'))
56+
def test_get_default_credentials_exist(self):
5857
self.assertEqual(
59-
path.join(path.expanduser('~'), '.credentials/credentials.json'),
58+
self.credentials_path,
6059
GoogleCalendar._get_default_credentials_path()
6160
)
6261

6362
def test_get_default_credentials_path_not_exist(self):
64-
self.assertFalse(path.exists(path.join(path.expanduser('~'), '.credentials')))
63+
self.fs.reset()
64+
with self.assertRaises(FileNotFoundError):
65+
GoogleCalendar._get_default_credentials_path()
66+
67+
def test_get_default_credentials_not_exist(self):
68+
self.fs.remove(self.credentials_path)
69+
with self.assertRaises(FileNotFoundError):
70+
GoogleCalendar._get_default_credentials_path()
71+
72+
def test_get_default_credentials_client_secrets(self):
73+
self.fs.remove(self.credentials_path)
74+
client_secret_path = path.join(self.credentials_dir, 'client_secret_1234.json')
75+
self.fs.create_file(client_secret_path)
6576
self.assertEqual(
66-
path.join(path.expanduser('~'), '.credentials/credentials.json'),
77+
client_secret_path,
6778
GoogleCalendar._get_default_credentials_path()
6879
)
69-
self.assertTrue(path.exists(path.join(path.expanduser('~'), '.credentials')))
80+
81+
def test_get_default_credentials_multiple_client_secrets(self):
82+
self.fs.remove(self.credentials_path)
83+
self.fs.create_file(path.join(self.credentials_dir, 'client_secret_1234.json'))
84+
self.fs.create_file(path.join(self.credentials_dir, 'client_secret_12345.json'))
85+
with self.assertRaises(ValueError):
86+
GoogleCalendar._get_default_credentials_path()
7087

7188
def test_get_token_valid(self):
7289
gc = GoogleCalendar(token_path=self.valid_token_path)

0 commit comments

Comments
 (0)