Skip to content

Latest commit

 

History

History
213 lines (155 loc) · 10.4 KB

File metadata and controls

213 lines (155 loc) · 10.4 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

Development Setup

python -m venv venv
venv\Scripts\activate                      # Windows
source venv/bin/activate                   # Unix/Mac
pip install -e .
pip install -r etsy_python/requirements.txt

Building

python setup.py build
python setup.py sdist bdist_wheel

Version Management

python scripts/check_version_consistency.py              # Validate versions match
python scripts/bump_version.py --dry-run --type patch    # Preview bump
python scripts/bump_version.py --type [patch|minor|major] # Execute bump
python scripts/release.py patch --no-push                # Local release test

Testing

pip install -r requirements-dev.txt                  # Install pytest + pytest-cov
pytest                                               # Run all tests
pytest -v                                            # Verbose output
pytest --cov=etsy_python --cov-report=term-missing   # Coverage report
pytest tests/test_session.py                         # Run specific test file
python scripts/check_version_consistency.py          # Validate versions match

Maintenance Workflow

# Check for Etsy API changes
python scripts/fetch_spec.py          # Fetch latest OAS spec
python scripts/diff_spec.py           # Diff against baseline

# Audit SDK coverage
python scripts/audit_sdk.py           # Compare spec vs SDK code

# After applying changes, update baseline
cp specs/latest.json specs/baseline.json

# Claude Code skills (interactive)
# /maintain-check          - Fetch + diff spec
# /maintain-release-check  - Check Etsy GitHub release notes
# /maintain-audit          - Full pipeline: fetch spec + releases, audit, review, implement

# GitHub Actions (see .github/workflows/)
# pr-tests.yml           - Tests + coverage on every PR push (posts/updates comment)
# maintenance-check.yml  - Weekly API change detection + SDK audit
# pr-coverage.yml        - SDK coverage report on PRs (add 'sdk-check' label)

Version Management

Single source of truth: etsy_python/_version.py

setup.py reads version dynamically from _version.py, which remains the single source of truth -- always trust _version.py. The .bumpversion.cfg file tracks version for the bump2version tool (which CI does not run); scripts/bump_version.py now keeps its current_version synced automatically on every bump, and the publish workflow commits both files together, so the two should no longer drift.

A pre-commit hook (.pre-commit-config.yaml) runs scripts/check_version_consistency.py to validate _version.py and .bumpversion.cfg agree.

Semantic Commit Messages

Commit messages trigger automatic version bumps on push to master:

  • breaking: or BREAKING CHANGE -> Major
  • feat: or contains feature -> Minor
  • All other commits -> Patch

Skip CI with [skip ci], [ci skip], or [no ci] in commit message. Version bump commits automatically include [skip ci] to prevent infinite loops.

Architecture

Layer Overview

Resources (API endpoints) -> Models (request validation) -> EtsyClient (HTTP + auth) -> Etsy API

All code lives under etsy_python/v3/:

Directory Purpose
auth/ OAuth 2.0 PKCE flow (EtsyOAuth)
common/ Utilities (Utils.py), environment config (Env.py), HTTP constants (Request.py)
enums/ Type-safe API parameter constants
exceptions/ BaseAPIException -> RequestException (with rate limits)
models/ Request data models with validation (Request base, FileRequest for uploads)
resources/ ~25 API endpoint classes + Session.py (EtsyClient) + Response.py

Key Components

EtsyClient (resources/Session.py): Central HTTP client. Manages OAuth tokens with auto-refresh, session headers, rate limit parsing. All resources receive it via constructor injection.

Request base class (models/Request.py): Provides nullable/mandatory field lists. check_mandatory() validates required fields, get_dict() serializes to API format excluding empty nullable fields. Note: mandatory list keys must match the Python attribute name (e.g. "_type", not "type").

FileRequest (models/FileRequest.py): Extends Request with file (multipart) and data attributes for upload endpoints.

Important Serialization Patterns

_type field mapping: Python's type is a builtin, so model attributes that map to the API's type field are stored as _type. todict() in Utils.py maps _type"type" during serialization. The mandatory list in Request models must use "_type" (the attribute name), not "type".

Boolean False vs nullable: get_nulled() in Request.py treats empty strings, empty lists, and zero as null for nullable fields — but explicitly excludes bool values. False is a valid distinct value from null and serializes as False, not None. The todict() function in Utils.py has the same guard: (value != 0 or isinstance(value, bool)).

Request Flow

  1. Resource method called with parameters
  2. Request model validates mandatory fields via super().__init__()
  3. EtsyClient.make_request() checks token expiry (UTC-aware), refreshes if needed
  4. HTTP request sent; response parsed into Response or raises RequestException
  5. Rate limits extracted from response headers when present

Resource Pattern

Every resource is a @dataclass with session: EtsyClient. Methods return Union[Response, RequestException].

  • GET/DELETE: Parameters passed as **kwargs, built into query string via generate_get_uri()
  • POST/PUT/PATCH: Payload is a Request model, serialized via .get_dict() to JSON
  • File uploads: Payload is a FileRequest, sent as multipart form data
  • Enums: Always use .value to extract the API string (e.g., State.ACTIVE.value -> "active")

Utility Functions (common/Utils.py)

  • generate_get_uri(uri, **kwargs) -- builds query string from kwargs dict
  • todict(obj) -- recursive serializer handling Enums, nested objects, nullable fields
  • generate_bytes_from_file(file) -- reads file bytes for uploads

Environment (common/Env.py)

Controlled by ETSY_ENV env var (default: "PROD"). Sets base URLs for OAuth (etsy.com) and API (openapi.etsy.com).

Coding Conventions

  • Classes: PascalCase. Methods: snake_case. Constants/Enum values: UPPER_SNAKE_CASE
  • Always use type hints for parameters and return values
  • Resource methods return Union[Response, RequestException] -- no try/catch in resource layer
  • Imports: stdlib -> third-party -> internal (absolute imports: from etsy_python.v3...)
  • All datetime comparisons use UTC timezone via datetime.now(tz=timezone.utc)

Adding a New API Endpoint

  1. Model in models/: Create a class extending Request with nullable and mandatory lists
  2. Enums in enums/: Add any new type-safe constants
  3. Resource method in resources/: Add method to existing or new @dataclass resource class
  4. Exports: Update resources/__init__.py if adding a new resource class

Dependencies

  • requests==2.32.4 -- HTTP client
  • requests-oauthlib>=1.3.1 -- OAuth 2.0

CI/CD

Publish Pipeline (python-publish.yml)

On push to master:

  1. Check for skip-ci markers
  2. Bump version based on commit message semantics
  3. Build sdist + wheel
  4. Publish to PyPI (trusted publishing)
  5. Create GitHub Release

Uses VERSION_BUMP_TOKEN (fine-grained PAT with Contents:Read/Write) to push version bump commits to protected master branch. Also supports manual workflow dispatch with version type selection.

PR Workflows

  • pr-tests.yml — Runs pytest with coverage on every PR push. Posts/updates a coverage comment on the PR using PATCH-first upsert to avoid race conditions. Has concurrency groups to cancel in-flight runs.
  • pr-coverage.yml — SDK coverage audit against Etsy OAS spec. Triggered by sdk-check label or manual dispatch. Fetches latest spec with fallback to specs/baseline.json.
  • maintenance-check.yml — Weekly scheduled check for Etsy API changes. Fetches spec, diffs against baseline, runs SDK audit.

Branch Protection

.github/CODEOWNERS requires @amitray007 review for changes to .github/workflows/ and .github/CODEOWNERS to protect CI secrets from unauthorized workflow modifications.

Scripts

Script Purpose
scripts/bump_version.py Semantic version bumping (supports --dry-run, --type auto)
scripts/check_version_consistency.py Validates _version.py matches .bumpversion.cfg
scripts/release.py Manual release orchestration (--no-push, --no-build, --no-tag, --force)
scripts/generate_release_notes.py Generates changelog from git commits (used by CI)
scripts/fetch_spec.py Downloads latest Etsy OAS spec to specs/latest.json
scripts/diff_spec.py Diffs specs/baseline.json vs specs/latest.json, outputs specs/diff-report.md
scripts/audit_sdk.py Audits SDK coverage against OAS spec, outputs specs/audit-report.md. Loads specs/audit-ignore.json to suppress reviewed findings (override with --ignore-file)
scripts/check_releases.py Checks Etsy GitHub releases for new changes, outputs specs/release-notes.md
scripts/format_pr_comment.py Formats audit report as a PR comment (used by CI)

Audit Suppressions (specs/audit-ignore.json)

Reviewed, accepted audit findings (deliberate deprecated aliases, intentionally partial enums, back-compat kwargs, etc.) live in specs/audit-ignore.json — never hard-coded in audit_sdk.py. Each run re-derives findings and only suppresses an entry while its finding still occurs; for the value-bearing types (enum_staleness, param_drift), only the listed values are hidden, so a newly added enum value or newly drifted parameter still surfaces. Entries matching nothing are reported under a Stale Ignores section so the list stays honest, and suppressed findings are listed (with reasons) under Suppressed (Verified). To accept a finding, add an entry (type + key, plus direction/values for the value-bearing types); to stop accepting it, delete the entry. A missing file means "no suppressions".

Supported type values: extra_method, enum_staleness, param_drift, code_issue. Prefer an explicit values list over "*" — a wildcard hides everything on that key, including drift nobody has reviewed.