diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index a11b503..0a1eed0 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b171096 --- /dev/null +++ b/CLAUDE.md @@ -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. diff --git a/livestyled/__init__.py b/livestyled/__init__.py index 14895e4..afced14 100644 --- a/livestyled/__init__.py +++ b/livestyled/__init__.py @@ -1 +1 @@ -__version__ = '1.4.12' +__version__ = '2.0.0' diff --git a/requirements.txt b/requirements.txt index 622daed..6512c6d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/requirements_dev.txt b/requirements_dev.txt index 9d9a7ac..8677da8 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -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 diff --git a/setup.py b/setup.py index bf4699c..a81a405 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ import os import re -from pkg_resources import parse_requirements from setuptools import find_packages, setup del os.link @@ -9,6 +8,18 @@ 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 @@ -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] """ diff --git a/tox.ini b/tox.ini index a4045c5..0afc6d7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] skipsdist = True envlist = - py39 + py314 [testenv] change_dir = {toxinidir}