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
6 changes: 3 additions & 3 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python: [3.7, 3.8]
python: ['3.9', '3.11', '3.13', '3.14']

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install Tox and any other packages
Expand Down
55 changes: 55 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# CLAUDE.md: python-sdk

The LiveStyled Python SDK (pip package `livestyled`): a shared REST client for apiv3's Hydra/JSON-LD
API, used by the platform's Python lambdas to read and write Spotlight resources (users, devices,
tickets, events, device realities, and so on) without each one re-implementing the HTTP + (de)serialization
layer. Historically named after LiveStyled (the company that became RealifeTech, then AudienceView).

## Role in the platform

This is a library, not a deployed service: it has no lambda handler, no `serverless.yml`, no runtime of
its own. It is published to public PyPI (`pypi.org`, project `livestyled`) and pulled in as a dependency by
the Python lambdas across the workspace, either directly (e.g. `connect.av.av_ticketing`,
`connect.realife.goodtill`, `connect.realife.axs`, `connect.realife.sso`, `platform.register_device_on_sns`,
`platform.cohort_actions`) or transitively via `reality-evaluator`
(`git@bitbucket.org:livestyled-dev/reality-evaluator.git`), which the Realities evaluators depend on.
It talks to apiv3, the platform's source of truth, over REST; it is not a GraphQL client (the Apollo
gateway is reached by other components). Every consumer pins an exact version, so a new SDK release never
reaches a consumer until that consumer explicitly bumps its own pin.

## Run it

- `pip install -r requirements_dev.txt` (all deps, runtime and dev, are on public PyPI; no private index
needed here, unlike the consumer lambdas)
- `tox` runs flake8 + the pytest suite (`livestyled/tests/`, ~49 tests exercising the model schemas'
serialization/deserialization). CI is GitHub Actions (`.github/workflows/unit_tests.yml`), matrixed
across supported Python versions.

There is no application to start: you exercise it by instantiating `LiveStyledAPIClient`
(`livestyled/client.py`) against an apiv3 base URL + API key and calling its resource methods, or by running
the tests, which mock the HTTP layer with `requests-mock`.

## Deploy

Publishing is manual, to public PyPI, per the internal wiki:

1. Update `__version__` in `livestyled/__init__.py` (semver).
2. Commit the version bump.
3. From the repo root: `python setup.py sdist; python3 -m twine upload dist/* --verbose; rm dist/*`

You need a PyPI account token with publish rights on the `livestyled` project (this is separate from GitHub
access). There is no automated release pipeline in the repo (the only GitHub Actions workflow runs the
tests); the `.devN` versions on PyPI come from the same manual script run off non-release branches.

## How it works

`livestyled/client.py` (`LiveStyledAPIResourceClient`, ~26 resource methods) is a thin REST client over
`requests`: `_api_get` / `_api_get_paginated` / `_api_post` / `_api_patch` / `_api_put` / `_api_delete`
issue the HTTP calls with the configured API key headers, and `_api_get_paginated` follows apiv3's
Hydra/JSON-LD pagination (`hydra:member` for the page, `hydra:view` -> `hydra:next` for the next page).
Each resource has a model under `livestyled/models/` (~45) and a marshmallow schema under
`livestyled/schemas/` (~46) that maps between the API's camelCase JSON-LD payloads and the SDK's snake_case
model attributes; the resource methods return either model instances or raw dicts depending on the call.
The runtime deps (`marshmallow`, `requests`) require Python >= 3.9, so from 2.0.0 the SDK no longer runs on
Python 3.7/3.8 that earlier versions supported (which is why that jump was released as a major); the
marshmallow pin is kept on the 3.x line deliberately, since 4.x would break the schemas' `missing=` usage.
2 changes: 1 addition & 1 deletion livestyled/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.4.12'
__version__ = '2.0.0'
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
marshmallow==3.3.0
marshmallow==3.26.2
marshmallow-polyfield==5.7
pytz
requests==2.22.0
requests==2.32.5
26 changes: 16 additions & 10 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
-r requirements.txt

coveralls==1.11.1
flake8==3.7.9
flake8-import-order==0.18.1
# setuptools>=82 dropped pkg_resources, which flake8-import-order 0.18.x imports at load time.
# Pin below 82 for the (dev-only) lint/test env; does not affect the shipped package.
setuptools<82
coveralls==3.3.1
flake8==7.1.1
flake8-import-order==0.18.2
flake8-mutable==1.2.0
flake8-pep3101==1.2.1
flake8-quotes==2.1.1
# flake8-pep3101 2.x uses ast.Num (removed in Python 3.12); 3.0.0 fixes that but requires >=3.10.
# No single version spans the whole CI matrix, so select per Python version.
flake8-pep3101==2.1.0; python_version < "3.12"
flake8-pep3101==3.0.0; python_version >= "3.12"
flake8-quotes==3.4.0
flake8-tuple==0.4.1
pytest==4.6.7
pytest-blockage==0.2.2
pytest-cov==2.8.1
requests-mock==1.7.0
tox==3.28.0
pytest==8.3.4
pytest-blockage==0.2.4
pytest-cov==5.0.0
requests-mock==1.12.1
tox==4.23.2
16 changes: 14 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import os
import re

from pkg_resources import parse_requirements
from setuptools import find_packages, setup

del os.link

PACKAGE_NAME = 'livestyled'


def parse_requirements(requirements_file):
"""Read requirements.txt into a list of requirement strings, skipping blanks,
comments and pip options (e.g. --extra-index-url). Replaces the old
pkg_resources.parse_requirements, which broke on setuptools>=82 (pkg_resources removed)."""
with open(requirements_file, 'r') as f:
return [
line.strip()
for line in f
if line.strip() and not line.startswith(('#', '-'))
]


def parse_version(package_name):
"""
if environment variable PACKAGE_VERSION set use that as version number
Expand Down Expand Up @@ -40,7 +51,8 @@ def parse_version(package_name):
author='LiveStyled',
author_email='dev@livestyled.com',
url='https://github.com/livestyled/python-sdk',
install_requires=[str(req) for req in parse_requirements(open(requirements_fn).read())],
python_requires='>=3.9',
install_requires=parse_requirements(requirements_fn),
entry_points="""
[console_scripts]
"""
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tox]
skipsdist = True
envlist =
py39
py314

[testenv]
change_dir = {toxinidir}
Expand Down
Loading