Skip to content

Latest commit

 

History

History
281 lines (222 loc) · 8.02 KB

File metadata and controls

281 lines (222 loc) · 8.02 KB

googleapiutils2

Python wrapper for Google APIs: Drive, Sheets, Gmail, Admin, Groups, Geocoding.

Installation

pip install googleapiutils2

Requires Python ^3.12

File Tree

googleapiutils2/
├── __init__.py              # Package exports
├── monitor.py               # DriveMonitor, SheetsMonitor (change detection)
├── utils/                   # Core infrastructure
│   ├── __init__.py          # Utils exports
│   ├── drive.py             # DriveBase, auth, exceptions, MIME utils
│   ├── decorators.py        # retry, cache_with_stale_interval
│   ├── misc.py              # GoogleMimeTypes, SCOPES, constants
│   └── utils.py             # Throttler, hex_to_rgb, URL parsing
├── drive/                   # Google Drive API
│   ├── __init__.py          # Drive, Permissions exports
│   ├── drive.py             # Drive class (upload, download, copy, list, etc.)
│   └── misc.py              # Constants, pagination helpers
├── sheets/                  # Google Sheets API
│   ├── __init__.py          # Sheets, SheetsValueRange, SheetSlice exports
│   ├── sheets.py            # Sheets class (CRUD, formatting, batching)
│   ├── sheets_value_range.py  # Range wrapper with slice notation
│   ├── sheets_slice.py      # Slice indexing implementation
│   └── misc.py              # A1 notation, enums, constants
├── mail/                    # Gmail API
│   ├── __init__.py          # Mail export
│   └── mail.py              # Mail class (send, draft, labels, messages)
├── admin/                   # Google Workspace Admin API
│   ├── __init__.py          # Admin export
│   └── admin.py             # Admin class (user management)
├── groups/                  # Google Groups API
│   ├── __init__.py          # Groups export
│   ├── groups.py            # Groups class (group/member management)
│   └── misc.py              # Constants
└── geocode/                 # Google Maps Geocoding API
    ├── __init__.py          # Geocode export
    ├── geocode.py           # Geocode class (address ↔ coordinates)
    └── misc.py              # TypedDicts, LocationType enum

test/                        # Integration tests (live API)
├── conftest.py              # Shared fixtures
├── drive/                   # Drive tests
├── sheets/                  # Sheets tests
└── geocode/                 # Geocode tests

examples/                    # Usage examples
├── drive_upload.py          # Upload files/folders
├── sheets_crud.py           # Sheet operations
├── mail.py                  # Send emails
├── monitor.py               # Change detection
└── ...

Authentication

Two methods supported:

Service Account (automation)

from googleapiutils2 import Drive, get_oauth2_creds

# Basic
creds = get_oauth2_creds(client_config="auth/service-account.json")
drive = Drive(creds=creds)

# Domain-wide delegation (Workspace only)
creds = get_oauth2_creds(client_config="auth/service-account.json")
creds = creds.with_subject("user@domain.com")
drive = Drive(creds=creds)

Setup: https://console.cloud.google.com/iam-admin/serviceaccounts

OAuth2 Client (user consent)

# First run: browser auth, saves token to auth/token.pickle
creds = get_oauth2_creds(
    client_config="auth/oauth2_credentials.json",
    token_path="auth/token.pickle"
)
drive = Drive(creds=creds)

Setup: https://console.cloud.google.com/apis/credentials/oauthclient (Desktop app)

Auto-discovery

# Checks ./auth/credentials.json or GOOGLE_API_CREDENTIALS env var
drive = Drive()
sheets = Sheets()

Quick Start

Drive

from googleapiutils2 import Drive, GoogleMimeTypes

drive = Drive()

# Upload
drive.upload("file.csv", to_mime_type=GoogleMimeTypes.sheets, parents=["folder_id"])
drive.upload("./folder", recursive=True, update=True)

# List
for file in drive.list(query="name contains 'report'"):
    print(f"{file['name']}: {file['id']}")

# Download
drive.download("file_id", "./output.pdf", mime_type=GoogleMimeTypes.pdf)
drive.download("folder_id", "./local_folder", recursive=True)

Sheets

from googleapiutils2 import Sheets, SheetsValueRange

sheets = Sheets()
Sheet1 = SheetsValueRange(sheets, sheet_url, "Sheet1")

# Slice notation
Sheet1[1, "A"].update([["Value"]])
Sheet1[2:5, 1:3].update([[1,2,3], [4,5,6], [7,8,9]])
data = Sheet1[...].read()

# Batch
sheets.batch_update(sheet_url, {
    Sheet1[1, ...]: [["Header 1", "Header 2"]],
    Sheet1[2:4, ...]: [[1, 2], [3, 4]]
})

# DataFrame
df = Sheet1[...].to_frame()
Sheet1.update(sheets.from_frame(df, include_header=True))

# Format
sheets.format(sheet_url, Sheet1[1, ...], bold=True, background_color="#d48686")

Mail

from googleapiutils2 import Mail

mail = Mail()

# Send
mail.send(
    sender="me@example.com",
    to="user@example.com",
    subject="Test",
    body="Hello"
)

# List
for msg in mail.list_messages(query="from:user@example.com after:2024/01/01"):
    print(msg['id'], msg['snippet'])

Admin

from googleapiutils2 import Admin

admin = Admin()

# Create user
user = admin.create_user(
    primary_email="test@domain.com",
    given_name="Test",
    family_name="User",
    password="temp123"
)

# List
for user in admin.list_users(query="givenName:John"):
    print(user['primaryEmail'])

Groups

from googleapiutils2 import Groups

groups = Groups()

# Create
group = groups.create(
    email="team@domain.com",
    name="Engineering",
    description="All engineers"
)

# Members
groups.members_insert("team@domain.com", "user@domain.com")
for member in groups.members_list("team@domain.com"):
    print(member['email'], member['role'])

Geocode

from googleapiutils2 import Geocode

geocoder = Geocode(api_key="YOUR_API_KEY")

# Forward
results = geocoder.geocode("1600 Amphitheatre Parkway, Mountain View, CA")
print(results[0]['geometry']['location'])  # {'lat': 37.422, 'lng': -122.084}

# Reverse
results = geocoder.reverse_geocode(lat=37.422, long=-122.084)

Monitor

from googleapiutils2 import SheetsMonitor

def on_change(data, monitor):
    print(f"Sheet updated: {len(data)} rows")

monitor = SheetsMonitor(sheets, drive, sheet_url, on_change, interval=30)
monitor.start()

Architecture

Base Class: DriveBase - All API classes inherit (except Geocode)

  • TTL caching (80s, 128 entries)
  • Retry decorator (10 retries, 30s delay, exponential backoff)
  • Throttling (0.1s individual requests, 1s batch operations)
  • Background request queueing via DriveThread

Exception Hierarchy:

  • GoogleAPIException (base)
    • InvalidRequestError
    • OverQueryLimitError
    • RequestDeniedError
    • NotFoundError
    • UnknownError

Key Patterns:

  • TYPE_CHECKING blocks: Circular import prevention
  • TTL caching: @cachedmethod on DriveBase
  • Pagination: Generator pattern via list_drive_items()
  • MIME conversion: Auto-detect upload, auto-export download
  • Slice notation: NumPy-like indexing for Sheets

Module Details

Dependencies

  • google-api-python-client ^2.168.0
  • google-auth ^2.39.0
  • google-auth-oauthlib ^1.2.1
  • pandas ^2.2.3
  • cachetools ^5.5.2
  • loguru ^0.7.3
  • requests ^2.32.3

Testing

pytest test/

Tests use real Google APIs with session-scoped fixtures and automatic cleanup.