# gspread_asyncio
An [asyncio wrapper](https://docs.python.org/3/library/asyncio.html) for [burnash's excellent Google Spreadsheet API library](https://github.com/burnash/gspread). `gspread_asyncio` isn't just a plain asyncio wrapper around the `gspread` API, it implements several useful and helpful features on top of those APIs. It's useful for long-running processes and one-off scripts.
Requires Python >= 3.8.
[![Documentation Status](https://readthedocs.org/projects/gspread-asyncio/badge/?version=latest)](https://gspread-asyncio.readthedocs.io/en/latest/?badge=latest) ![CI status](https://github.com/dgilman/gspread_asyncio/actions/workflows/ci.yml/badge.svg)
## Features
* Complete async wrapping of the `gspread` API. All `gspread` API calls are run off the main thread in a threadpool executor.
* Internal caching and reuse of `gspread` `Client`/`Spreadsheet`/`Worksheet` objects.
* Automatic renewal of expired credentials.
* Automatic retries of spurious failures from Google's servers (HTTP 5xx).
* Automatic rate limiting with defaults set to Google's default API limits.
* Many methods that don't need to return a value can optionally return an already-scheduled `Future` (the `nowait` kwarg). You can ignore that future, allowing forward progress on your calling coroutine while the asyncio event loop schedules and runs the Google Spreadsheet API call at a later time for you.
## Example usage
```python
import asyncio
import gspread_asyncio
# from google-auth package
from google.oauth2.service_account import Credentials
# First, set up a callback function that fetches our credentials off the disk.
# gspread_asyncio needs this to re-authenticate when credentials expire.
def get_creds():
# To obtain a service account JSON file, follow these steps:
# https://gspread.readthedocs.io/en/latest/oauth2.html#for-bots-using-service-account
creds = Credentials.from_service_account_file("serviceacct_spreadsheet.json")
scoped = creds.with_scopes([
"https://spreadsheets.google.com/feeds",
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive",
])
return scoped
# Create an AsyncioGspreadClientManager object which
# will give us access to the Spreadsheet API.
agcm = gspread_asyncio.AsyncioGspreadClientManager(get_creds)
# Here's an example of how you use the API:
async def example(agcm):
# Always authorize first.
# If you have a long-running program call authorize() repeatedly.
agc = await agcm.authorize()
ss = await agc.create("Test Spreadsheet")
print("Spreadsheet URL: https://docs.google.com/spreadsheets/d/{0}".format(ss.id))
print("Open the URL in your browser to see gspread_asyncio in action!")
# Allow anyone with the URL to write to this spreadsheet.
await agc.insert_permission(ss.id, None, perm_type="anyone", role="writer")
# Create a new spreadsheet but also grab a reference to the default one.
ws = await ss.add_worksheet("My Test Worksheet", 10, 5)
zero_ws = await ss.get_worksheet(0)
# Write some stuff to both spreadsheets.
for row in range(1, 11):
for col in range(1, 6):
val = "{0}/{1}".format(row, col)
await ws.update_cell(row, col, val + " ws")
await zero_ws.update_cell(row, col, val + " zero ws")
print("All done!")
# Turn on debugging if you're new to asyncio!
asyncio.run(example(agcm), debug=True)
```
## Observational notes and gotchas
* This module does not define its own exceptions, it propagates instances of `gspread.exceptions.GSpreadException`.
* Always call `AsyncioGspreadClientManager.authorize()`, `AsyncioGspreadClient.open_*()` and `AsyncioGspreadSpreadsheet.get_worksheet()` before doing any work on a spreadsheet. These methods keep an internal cache so it is painless to call them many times, even inside of a loop. This makes sure you always have a valid set of authentication credentials from Google.
* The only object you should store in your application is the `AsyncioGspreadClientManager` (`agcm`).
* Right now the `gspread` library does not support bulk appends of rows or bulk changes of cells. When this is done `gspread_asyncio` will support batching of these Google API calls without any changes to the Python `gspread_asyncio` API.
* I came up with the default 1.1 second delay between API calls (the `gspread_delay` kwarg) after extensive experimentation. The official API rate limit is one call every second but however Google measures these things introduces a tiny bit of jitter that will get you rate blocked if you ride that limit exactly.
* Google's service reliability on these endpoints is surprisingly bad. There are frequent HTTP 500s and the retry logic will save your butt in long-running scripts or short, one-shot, one-off ones.
* Experimentation also found that Google's credentials expire after an hour and the default `reauth_interval` of 45 minutes takes care of that just fine.
## License
MIT
## Sponsorship
Development of gspread_asyncio is sponsored by [Pro Football History.com, your source for NFL coaching biographies.](https://pro-football-history.com)
Raw data
{
"_id": null,
"home_page": "https://github.com/dgilman/gspread_asyncio",
"name": "gspread-asyncio",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "",
"keywords": "spreadsheets,google-spreadsheets,asyncio",
"author": "David Gilman",
"author_email": "dgilman@gilslotd.com",
"download_url": "https://files.pythonhosted.org/packages/bf/7e/480054799ce34cec733d9d33951a801ffd2aea16dcdeea6801807aa44d83/gspread_asyncio-2.0.0.tar.gz",
"platform": null,
"description": "# gspread_asyncio\n\nAn [asyncio wrapper](https://docs.python.org/3/library/asyncio.html) for [burnash's excellent Google Spreadsheet API library](https://github.com/burnash/gspread). `gspread_asyncio` isn't just a plain asyncio wrapper around the `gspread` API, it implements several useful and helpful features on top of those APIs. It's useful for long-running processes and one-off scripts.\n\nRequires Python >= 3.8.\n\n[![Documentation Status](https://readthedocs.org/projects/gspread-asyncio/badge/?version=latest)](https://gspread-asyncio.readthedocs.io/en/latest/?badge=latest) ![CI status](https://github.com/dgilman/gspread_asyncio/actions/workflows/ci.yml/badge.svg)\n\n\n## Features\n\n* Complete async wrapping of the `gspread` API. All `gspread` API calls are run off the main thread in a threadpool executor.\n* Internal caching and reuse of `gspread` `Client`/`Spreadsheet`/`Worksheet` objects.\n* Automatic renewal of expired credentials.\n* Automatic retries of spurious failures from Google's servers (HTTP 5xx).\n* Automatic rate limiting with defaults set to Google's default API limits.\n* Many methods that don't need to return a value can optionally return an already-scheduled `Future` (the `nowait` kwarg). You can ignore that future, allowing forward progress on your calling coroutine while the asyncio event loop schedules and runs the Google Spreadsheet API call at a later time for you.\n\n## Example usage\n\n```python\nimport asyncio\n\nimport gspread_asyncio\n\n# from google-auth package\nfrom google.oauth2.service_account import Credentials \n\n# First, set up a callback function that fetches our credentials off the disk.\n# gspread_asyncio needs this to re-authenticate when credentials expire.\n\ndef get_creds():\n # To obtain a service account JSON file, follow these steps:\n # https://gspread.readthedocs.io/en/latest/oauth2.html#for-bots-using-service-account\n creds = Credentials.from_service_account_file(\"serviceacct_spreadsheet.json\")\n scoped = creds.with_scopes([\n \"https://spreadsheets.google.com/feeds\",\n \"https://www.googleapis.com/auth/spreadsheets\",\n \"https://www.googleapis.com/auth/drive\",\n ])\n return scoped\n\n# Create an AsyncioGspreadClientManager object which\n# will give us access to the Spreadsheet API.\n\nagcm = gspread_asyncio.AsyncioGspreadClientManager(get_creds)\n\n# Here's an example of how you use the API:\n\nasync def example(agcm):\n # Always authorize first.\n # If you have a long-running program call authorize() repeatedly.\n agc = await agcm.authorize()\n\n ss = await agc.create(\"Test Spreadsheet\")\n print(\"Spreadsheet URL: https://docs.google.com/spreadsheets/d/{0}\".format(ss.id))\n print(\"Open the URL in your browser to see gspread_asyncio in action!\")\n\n # Allow anyone with the URL to write to this spreadsheet.\n await agc.insert_permission(ss.id, None, perm_type=\"anyone\", role=\"writer\")\n\n # Create a new spreadsheet but also grab a reference to the default one.\n ws = await ss.add_worksheet(\"My Test Worksheet\", 10, 5)\n zero_ws = await ss.get_worksheet(0)\n\n # Write some stuff to both spreadsheets.\n for row in range(1, 11):\n for col in range(1, 6):\n val = \"{0}/{1}\".format(row, col)\n await ws.update_cell(row, col, val + \" ws\")\n await zero_ws.update_cell(row, col, val + \" zero ws\")\n print(\"All done!\")\n\n# Turn on debugging if you're new to asyncio!\nasyncio.run(example(agcm), debug=True)\n```\n\n## Observational notes and gotchas\n\n* This module does not define its own exceptions, it propagates instances of `gspread.exceptions.GSpreadException`.\n* Always call `AsyncioGspreadClientManager.authorize()`, `AsyncioGspreadClient.open_*()` and `AsyncioGspreadSpreadsheet.get_worksheet()` before doing any work on a spreadsheet. These methods keep an internal cache so it is painless to call them many times, even inside of a loop. This makes sure you always have a valid set of authentication credentials from Google.\n* The only object you should store in your application is the `AsyncioGspreadClientManager` (`agcm`).\n* Right now the `gspread` library does not support bulk appends of rows or bulk changes of cells. When this is done `gspread_asyncio` will support batching of these Google API calls without any changes to the Python `gspread_asyncio` API.\n* I came up with the default 1.1 second delay between API calls (the `gspread_delay` kwarg) after extensive experimentation. The official API rate limit is one call every second but however Google measures these things introduces a tiny bit of jitter that will get you rate blocked if you ride that limit exactly.\n* Google's service reliability on these endpoints is surprisingly bad. There are frequent HTTP 500s and the retry logic will save your butt in long-running scripts or short, one-shot, one-off ones.\n* Experimentation also found that Google's credentials expire after an hour and the default `reauth_interval` of 45 minutes takes care of that just fine.\n\n## License\n\nMIT\n\n## Sponsorship\n\nDevelopment of gspread_asyncio is sponsored by [Pro Football History.com, your source for NFL coaching biographies.](https://pro-football-history.com)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "asyncio wrapper for burnash's Google Spreadsheet API library, gspread",
"version": "2.0.0",
"project_urls": {
"Documentation": "https://gspread-asyncio.readthedocs.io/en/latest/",
"Homepage": "https://github.com/dgilman/gspread_asyncio",
"Source": "https://github.com/dgilman/gspread_asyncio",
"Tracker": "https://github.com/dgilman/gspread_asyncio/issues"
},
"split_keywords": [
"spreadsheets",
"google-spreadsheets",
"asyncio"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a28279c5d77c2f7175e14f9af971c692492c1eaf8a8bd4ccd5ec8d4c22e962a6",
"md5": "7b87c88ad21cef706be801aa8d6db560",
"sha256": "63713a5337d52d538bb26815d96f98950cbdfae67600e1c137cea8608f41e5c4"
},
"downloads": -1,
"filename": "gspread_asyncio-2.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "7b87c88ad21cef706be801aa8d6db560",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 23045,
"upload_time": "2024-02-10T20:51:00",
"upload_time_iso_8601": "2024-02-10T20:51:00.151462Z",
"url": "https://files.pythonhosted.org/packages/a2/82/79c5d77c2f7175e14f9af971c692492c1eaf8a8bd4ccd5ec8d4c22e962a6/gspread_asyncio-2.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "bf7e480054799ce34cec733d9d33951a801ffd2aea16dcdeea6801807aa44d83",
"md5": "7b8a7d588e9949040d6af01cef29c2e4",
"sha256": "693d81a80b17f65cdbb16a292b8cb8a17ffe886dd5aeadbc4a06188ebbc4b2b6"
},
"downloads": -1,
"filename": "gspread_asyncio-2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "7b8a7d588e9949040d6af01cef29c2e4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 25402,
"upload_time": "2024-02-10T20:51:01",
"upload_time_iso_8601": "2024-02-10T20:51:01.918958Z",
"url": "https://files.pythonhosted.org/packages/bf/7e/480054799ce34cec733d9d33951a801ffd2aea16dcdeea6801807aa44d83/gspread_asyncio-2.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-02-10 20:51:01",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "dgilman",
"github_project": "gspread_asyncio",
"travis_ci": true,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "cachetools",
"specs": [
[
"==",
"5.3.2"
]
]
},
{
"name": "certifi",
"specs": [
[
"==",
"2024.2.2"
]
]
},
{
"name": "charset-normalizer",
"specs": [
[
"==",
"3.3.2"
]
]
},
{
"name": "google-auth",
"specs": [
[
"==",
"2.27.0"
]
]
},
{
"name": "google-auth-oauthlib",
"specs": [
[
"==",
"1.2.0"
]
]
},
{
"name": "gspread",
"specs": [
[
"==",
"6.0.1"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.6"
]
]
},
{
"name": "oauthlib",
"specs": [
[
"==",
"3.2.2"
]
]
},
{
"name": "pyasn1",
"specs": [
[
"==",
"0.5.1"
]
]
},
{
"name": "pyasn1-modules",
"specs": [
[
"==",
"0.3.0"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.31.0"
]
]
},
{
"name": "requests-oauthlib",
"specs": [
[
"==",
"1.3.1"
]
]
},
{
"name": "rsa",
"specs": [
[
"==",
"4.9"
]
]
},
{
"name": "strenum",
"specs": [
[
"==",
"0.4.15"
]
]
},
{
"name": "urllib3",
"specs": [
[
"==",
"2.2.0"
]
]
}
],
"lcname": "gspread-asyncio"
}