ProbablyDavid
.com

[personal website of David Lu!!]

ProbablyDavid

ProbablyDavid
.com

[personal website of David Lu!!]

👀 googly 👀

4 minutes
September 16, 2024

A year and a half between blog entries? That’s pretty standard, although I suppose I did move to Canada in that time.

The Zen of Python refers to a list of principles for Python-y code. Some of these were on my mind when I was trying to figure out some Google APIs.

  • Simple is better than complex.
  • Flat is better than nested.
  • Readability counts.

Here’s Google’s provided code from their quickstart for retrieving values from a spreadsheet.

import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"]

# The ID and range of a sample spreadsheet.
SAMPLE_SPREADSHEET_ID = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
SAMPLE_RANGE_NAME = "Class Data!A2:E"


def main():
  """Shows basic usage of the Sheets API.
  Prints values from a sample spreadsheet.
  """
  creds = None
  # The file token.json stores the user's access and refresh tokens, and is
  # created automatically when the authorization flow completes for the first
  # time.
  if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
  # If there are no (valid) credentials available, let the user log in.
  if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
      creds.refresh(Request())
    else:
      flow = InstalledAppFlow.from_client_secrets_file(
          "credentials.json", SCOPES
      )
      creds = flow.run_local_server(port=0)
    # Save the credentials for the next run
    with open("token.json", "w") as token:
      token.write(creds.to_json())

  try:
    service = build("sheets", "v4", credentials=creds)

    # Call the Sheets API
    sheet = service.spreadsheets()
    result = (
        sheet.values()
        .get(spreadsheetId=SAMPLE_SPREADSHEET_ID, range=SAMPLE_RANGE_NAME)
        .execute()
    )
    values = result.get("values", [])

    if not values:
      print("No data found.")
      return

    print("Name, Major:")
    for row in values:
      # Print columns A and E, which correspond to indices 0 and 4.
      print(f"{row[0]}, {row[4]}")
  except HttpError as err:
    print(err)


if __name__ == "__main__":
  main()

Much of the code is boilerplate for getting/saving authentication information. The actual call to get values involves a lot of nested subcomponents.

  1. service
  2. spreadsheets
  3. values
  4. get
  5. execute
  6. get

It was all too complex, and too unreadable for me. I’d copy and paste half the code every time I had to access Google’s APIs. So given my recent foray into releasing Python libraries in pip I decided to make an easy-to-use library for Google APIs. Here’s my equivalent code, using the googly library.

from googly import SheetsAPI

# The ID and range of a sample spreadsheet.
SAMPLE_SPREADSHEET_ID = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
SAMPLE_RANGE_NAME = "Class Data!A2:E"


def main():
  """Shows basic usage of the Sheets API.
  Prints values from a sample spreadsheet.
  """
  service = SheetsAPI()
  service.set_sheet_id(SAMPLE_SPREADSHEET_ID)
  values = service.get_range(SAMPLE_RANGE_NAME)

  if not values:
    print("No data found.")
    return

  print("Name, Major:")
  for row in values:
    # Print columns A and E, which correspond to indices 0 and 4.
    print(f"{row[0]}, {row[4]}")


if __name__ == "__main__":
  main()

Right now, there are seven modules, basically corresponding with various tasks I’ve needed the library for over the years.

Extending it to other APIs is relatively trivial, I just haven’t had reason to do so yet. However, I have come across a few things that the official APIs just won’t do.