Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
name: Test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install
run: python -m pip install -e ".[dev]"
- name: Lint
run: |
ruff check .
ruff format --check .
- name: Type-check
run: mypy
- name: Test
run: pytest --cov

build:
name: Build distributions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Build
run: |
python -m pip install build twine
python -m build
python -m twine check --strict dist/*
119 changes: 119 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Publishes to PyPI when a version tag is pushed:
#
# git tag v0.1.0 && git push origin v0.1.0
#
# Uses PyPI trusted publishing (OIDC), so no API token is stored in GitHub. One-time
# setup on pypi.org (and test.pypi.org) under the Photon Commerce account:
# Publishing -> Add a pending publisher -> project "photon-sdk", owner
# "Photon-Commerce", repository "photon-sdk-python", workflow "release.yml",
# environment "pypi" (TestPyPI: "testpypi").
# Then create the "pypi" and "testpypi" environments under the repo's Settings ->
# Environments (adding required reviewers to "pypi" makes each release need approval).
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: read

jobs:
build:
name: Build distributions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Check the tag matches the package version
run: |
version=$(python -c "import re; print(re.search(r'__version__ = \"(.+?)\"', open('src/photon/_version.py').read()).group(1))")
if [ "v${version}" != "${GITHUB_REF_NAME}" ]; then
echo "Tag ${GITHUB_REF_NAME} does not match package version ${version}" >&2
exit 1
fi
- name: Build
run: |
python -m pip install build twine
python -m build
python -m twine check --strict dist/*
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

testpypi:
name: Publish to TestPyPI
needs: build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/photon-sdk
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

smoke-test:
name: Install from TestPyPI
needs: testpypi
runs-on: ubuntu-latest
steps:
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install and import
run: |
version="${GITHUB_REF_NAME#v}"
# TestPyPI's index can lag the upload by a minute.
for attempt in 1 2 3 4 5; do
python -m pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ "photon-sdk==${version}" && break
sleep 30
done
python -c "import photon, sys; assert photon.__version__ == sys.argv[1]; print(photon.__version__)" "${version}"

pypi:
name: Publish to PyPI
needs: smoke-test
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/photon-sdk
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1

github-release:
name: Create GitHub release
needs: pypi
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Create release from the changelog
env:
GH_TOKEN: ${{ github.token }}
run: |
version="${GITHUB_REF_NAME#v}"
awk -v v="$version" '$0 ~ "^## \\[" v "\\]" {found=1; next} found && /^## \[/ {exit} found' \
CHANGELOG.md > notes.md
gh release create "$GITHUB_REF_NAME" dist/* --title "$GITHUB_REF_NAME" --notes-file notes.md
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Changelog

All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
[Semantic Versioning](https://semver.org/).

## [0.1.0] - 2026-10-19

First public release.

### Added

- `PhotonClient`, a synchronous, fully typed client configured from arguments or
`PHOTON_*` environment variables (`PhotonClient.from_env()`), for the sandbox
and production environments.
- `submit()` — upload a document (path, binary file, bytes) or submit a URL, with
webhook, reference ID, subaccount and page-range options.
- `retrieve()` — fetch a result by `photon_key`; raises `NotReadyError` while it
is still processing.
- `extract()` — submit and poll until the result is ready, with a configurable
timeout; `timeout=None` returns the `Submission` for webhook flows.
- `InvoiceDocument` — typed results for invoices and receipt-expenses covering
every documented field, with exact `Decimal` amounts, ISO dates as
`datetime.date`, typed `line_items` and `tax_lines`, dict access by the API's
field names, `.raw`, `to_dict()` and `to_json()`.
- `RawDocument` — dict-style access to results for every other doctype.
- Data correction: `update_fields()`, `update_line_item()`, `add_line_item()` and
`delete_line_item()`.
- A typed exception hierarchy under `PhotonError`, classifying the API's
body-reported failures (quota, invalid key, unsupported file, ...).
- Retries with exponential backoff and jitter for 5xx and network errors. Requests
that could take effect twice are only retried when the connection failed.
- `DEBUG` logging of retries on the `photon` logger, never including credentials
or document keys.

[0.1.0]: https://github.com/Photon-Commerce/photon-sdk-python/releases/tag/v0.1.0
Loading
Loading