Skip to content
Merged

v4.15.0 #1476

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
73 changes: 59 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:

# if changing the below change the run-integration-tests versions and the check-deploy versions
# Make sure that we are running the integration tests on the first and last versions of the matrix
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
python: ['3.11', '3.12', '3.13', '3.14', '3.15']

runs-on: ${{ matrix.os }}

Expand Down Expand Up @@ -84,7 +84,7 @@ jobs:
path: |
${{ steps.get-dependencies.outputs.site_packages_loc }}
${{ steps.get-dependencies.outputs.site_bin_dir }}
key: ${{ runner.os }}-${{ matrix.python }}-build-${{ env.cache-name }}-${{ hashFiles('setup.py', 'setup.cfg') }}-v33
key: ${{ runner.os }}-${{ matrix.python }}-build-${{ env.cache-name }}-${{ hashFiles('setup.py', 'setup.cfg') }}-v34

- name: Install py-dependencies
if: steps.cache-dependencies.outputs.cache-hit != 'true'
Expand All @@ -109,7 +109,7 @@ jobs:
pytest -sv --cov-append --cov=. --cov-report xml tests/unit
- name: Check for Secret availability
id: secret-check
if: ${{ contains(fromJSON('["3.10"]'), matrix.python) || contains(fromJSON('["3.14"]'), matrix.python) }}
if: ${{ contains(fromJSON('["3.11"]'), matrix.python) || contains(fromJSON('["3.15"]'), matrix.python) }}
# perform secret check & put boolean result as an output
shell: bash
run: |
Expand All @@ -126,7 +126,7 @@ jobs:
fi

- name: Download Failed Tests from Previous Attempt
if: ${{ github.run_attempt > 1 && (contains(fromJSON('["3.10"]'), matrix.python) || contains(fromJSON('["3.14"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true'}}
if: ${{ github.run_attempt > 1 && (contains(fromJSON('["3.11"]'), matrix.python) || contains(fromJSON('["3.15"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true'}}
uses: actions/download-artifact@v4
with:
name: failed-tests-${{ matrix.os }}-${{ matrix.python }}
Expand All @@ -142,25 +142,25 @@ jobs:
shell: bash

# keep versions consistent with the first and last from the strategy matrix
if: ${{ (contains(fromJSON('["3.10"]'), matrix.python) || contains(fromJSON('["3.14"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true' && github.event_name != 'release'}}
if: ${{ (contains(fromJSON('["3.11"]'), matrix.python) || contains(fromJSON('["3.15"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true' && github.event_name != 'release'}}
run: |
# Set SYNAPSE_PROFILE based on OS and Python version
if [ "${{ startsWith(matrix.os, 'ubuntu') }}" == "true" ]; then
if [ "${{ matrix.python }}" == "3.10" ]; then
if [ "${{ matrix.python }}" == "3.11" ]; then
export SYNAPSE_PROFILE="TestUbuntuMinimumPython"
elif [ "${{ matrix.python }}" == "3.14" ]; then
elif [ "${{ matrix.python }}" == "3.15" ]; then
export SYNAPSE_PROFILE="TestUbuntuMaximumPython"
fi
elif [ "${{ startsWith(matrix.os, 'windows') }}" == "true" ]; then
if [ "${{ matrix.python }}" == "3.10" ]; then
if [ "${{ matrix.python }}" == "3.11" ]; then
export SYNAPSE_PROFILE="TestWindowsMinimumPython"
elif [ "${{ matrix.python }}" == "3.14" ]; then
elif [ "${{ matrix.python }}" == "3.15" ]; then
export SYNAPSE_PROFILE="TestWindowsMaximumPython"
fi
elif [ "${{ startsWith(matrix.os, 'macos') }}" == "true" ]; then
if [ "${{ matrix.python }}" == "3.10" ]; then
if [ "${{ matrix.python }}" == "3.11" ]; then
export SYNAPSE_PROFILE="TestMacosMinimumPython"
elif [ "${{ matrix.python }}" == "3.14" ]; then
elif [ "${{ matrix.python }}" == "3.15" ]; then
export SYNAPSE_PROFILE="TestMacosMaximumPython"
fi
fi
Expand Down Expand Up @@ -313,6 +313,51 @@ jobs:
name: coverage-report
path: coverage.xml

# run unit tests against older pandas versions, since the test job installs only the newest pandas in the supported range
test-pandas-versions:
needs: [pre-commit]
timeout-minutes: 60

strategy:
fail-fast: false
matrix:
include:
# pandas < 2.2.2 is not compatible with numpy 2, so those entries pin numpy 1.26.4 (the last 1.x release)
# pandas 1.5 is the lower bound in setup.cfg and installs only on Python 3.11
- python: '3.11'
pandas: 'pandas==1.5.3'
numpy: 'numpy==1.26.4'
# pandas 2.1 is the first version with DataFrame.map and the first to support Python 3.12
- python: '3.12'
pandas: 'pandas==2.1.4'
numpy: 'numpy==1.26.4'
# pandas 2.2.3 is the first version to support Python 3.13
- python: '3.13'
pandas: 'pandas==2.2.3'
numpy: 'numpy==2.5.4'

runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}

- name: Install py-dependencies
shell: bash
run: |
python -m pip install --upgrade pip

pip install -e ".[boto3,pandas,pysftp,tests,curator]" "${{ matrix.pandas }}" "${{ matrix.numpy }}"

- name: run-unit-tests
shell: bash
run: |
python -c "import numpy, pandas; print(f'pandas {pandas.__version__}, numpy {numpy.__version__}')"
pytest -sv tests/unit

sonarcloud:
needs: [test]
if: ${{ always() && !cancelled()}}
Expand Down Expand Up @@ -350,7 +395,7 @@ jobs:

# on a GitHub release, build the pip package and upload it as a GitHub release asset
package:
needs: [test, pre-commit]
needs: [test, test-pandas-versions, pre-commit]

runs-on: ubuntu-22.04

Expand All @@ -365,7 +410,7 @@ jobs:

- uses: actions/setup-python@v5
with:
python-version: '3.10'
python-version: '3.11'

- name: set-release-env
shell: bash
Expand Down Expand Up @@ -502,7 +547,7 @@ jobs:
os: [ubuntu-24.04, macos-15-intel, windows-2022]

# python versions should be consistent with the strategy matrix and the runs-integration-tests versions
python: ['3.10', '3.11', '3.12', '3.13', '3.14']
python: ['3.11', '3.12', '3.13', '3.14', '3.15']

runs-on: ${{ matrix.os }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.14'
python-version: '3.15'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
14 changes: 11 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<!-- Last reviewed: 2026-08 -->
<!-- Last reviewed: 2026-10 -->

## Project

Synapse Python Client — official Python SDK and CLI for Synapse (synapse.org), a collaborative science platform by Sage Bionetworks. Provides programmatic access to entities (projects, files, folders, tables, views), metadata, permissions, evaluations, and data curation workflows. Published to PyPI as `synapseclient`.

## Stack

- Python 3.10–3.14 (`setup.cfg`: `python_requires = >=3.10, <3.15`)
- Python 3.11–3.15 (`setup.cfg`: `python_requires = >=3.11, <3.16`)
- HTTP: httpx (async), requests (sync/legacy)
- Models: stdlib dataclasses (NOT Pydantic)
- pandas (optional): `pandas>=1.5,<4.0` (`setup.cfg`, 4 places)
- Tests: pytest 9.0.3, pytest-asyncio, pytest-socket, pytest-xdist
- Docs: MkDocs with Material theme, mkdocstrings
- Linting: ruff, black (line-length 88), isort (profile=black), bandit
Expand Down Expand Up @@ -107,6 +108,12 @@ pandas, boto3, pysftp, and other optional extras must never be imported at the t

For type annotations referencing pandas types, use `DATA_FRAME_TYPE` and `SERIES_TYPE` from `core/typing_utils.py` rather than `pd.DataFrame` or `pd.Series`. These aliases resolve correctly at type-check time without requiring pandas at runtime.

### pandas version compatibility
Code must work on every pandas version from 1.5 to 3.x. Use one code path that works on all supported versions. Use a feature check only when no such code path exists. Known pandas 3 traps:
- Text columns have the `str` dtype, not `object`. Use `pd.api.types.is_string_dtype()` instead of comparing to `object`.
- `DataFrame.applymap` is replaced by `DataFrame.map`, but `DataFrame.map` is not in pandas < 2.1. Use `df.apply(lambda col: col.map(func))` instead.
- `iterrows()` gives a `str`-dtype row when all values in the row are strings or missing, and `None` becomes `NaN`. Check each value, not only the column dtype.

## Constraints

- Do not use Pydantic for models — the codebase uses stdlib dataclasses with custom serialization. Mixing would break the `@async_to_sync` decorator and `fill_from_dict()` pattern.
Expand All @@ -125,7 +132,8 @@ For type annotations referencing pandas types, use `DATA_FRAME_TYPE` and `SERIES
- Integration tests use `--reruns 3` for flaky retries and `-n 4 --dist loadscope` for parallelism
- Integration fixtures create per-worker Synapse projects; use `schedule_for_cleanup()` for teardown
- Auth env vars: `SYNAPSE_AUTH_TOKEN` (bearer token), `SYNAPSE_PROFILE` (config file profile, default: `"default"`), `SYNAPSE_TOKEN_AWS_SSM_PARAMETER_NAME` (AWS SSM path)
- CI runs integration tests only on Python 3.10 and 3.14 (oldest + newest) to limit Synapse server load
- CI runs integration tests only on Python 3.11 and 3.15 (oldest + newest) to limit Synapse server load
- CI job `test-pandas-versions` (`.github/workflows/build.yml`) runs the unit tests on older pandas versions: 1.5.3 on Python 3.11, 2.1.4 on Python 3.12, 2.2.3 on Python 3.13 (ubuntu only). The `package` job needs it. pandas < 2.2.2 requires `numpy<2`, so each matrix entry pins its own numpy version (1.26.4, 1.26.4, 2.5.4).

## Maintenance

Expand Down
16 changes: 8 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ copied to forks).
#### Installing the Python Client in a virtual environment with pipenv
Perform the following one-time steps to set up your local environment.

1. This package uses Python, if you have not already, please install [pyenv](https://github.com/pyenv/pyenv#installation) to manage your Python versions. Versions supported by this package are all versions >=3.10 and <=3.14. If you do not install `pyenv` make sure that Python and `pip` are installed correctly and have been added to your PATH by running `python3 --version` and `pip3 --version`. If your installation was successful, your terminal will return the versions of Python and `pip` that you installed. **Note**: If you have `pyenv` it will install a specific version of Python for you.
1. This package uses Python, if you have not already, please install [pyenv](https://github.com/pyenv/pyenv#installation) to manage your Python versions. Versions supported by this package are all versions >=3.11 and <=3.15. If you do not install `pyenv` make sure that Python and `pip` are installed correctly and have been added to your PATH by running `python3 --version` and `pip3 --version`. If your installation was successful, your terminal will return the versions of Python and `pip` that you installed. **Note**: If you have `pyenv` it will install a specific version of Python for you.

2. Install `pipenv` by running `pip install pipenv`.
- If you already have `pipenv` installed, ensure that the version is >=2023.9.8 to avoid compatibility issues.
Expand Down Expand Up @@ -271,18 +271,18 @@ When adding support for a new Python version or dropping support for an old vers

#### Adding a new Python version

When adding support for a new Python version (e.g., adding Python 3.15), update the following:
When adding support for a new Python version (e.g., adding Python 3.16), update the following:

**Code configuration files:**
1. **`setup.cfg`**:
- Add the new version to the `classifiers` list under `[metadata]` (e.g., `Programming Language :: Python :: 3.15`)
- Update the `python_requires` constraint under `[options]` to include the new version (e.g., `>=3.10, <3.16`)
- Add the new version to the `classifiers` list under `[metadata]` (e.g., `Programming Language :: Python :: 3.16`)
- Update the `python_requires` constraint under `[options]` to include the new version (e.g., `>=3.11, <3.17`)

2. **`pyproject.toml`**:
- Update the `target-version` list in the `[tool.black]` section to include the new version if needed

3. **`Dockerfile`**:
- Update the base image to use the new Python version (e.g., `FROM python:3.15-slim`)
- Update the base image to use the new Python version (e.g., `FROM python:3.16-slim`)

**CI/CD configuration files:**
1. **`.github/workflows/build.yml`**:
Expand All @@ -296,12 +296,12 @@ When adding support for a new Python version (e.g., adding Python 3.15), update

#### Dropping an old Python version

When dropping support for an old Python version (e.g., removing Python 3.10), update the following:
When dropping support for an old Python version (e.g., removing Python 3.11), update the following:

**Code configuration files:**
1. **`setup.cfg`**:
- Remove the old version from the `classifiers` list under `[metadata]`
- Update the `python_requires` constraint under `[options]` to reflect the new minimum version (e.g., `>=3.11, <3.15`)
- Update the `python_requires` constraint under `[options]` to reflect the new minimum version (e.g., `>=3.12, <3.16`)

2. **`pyproject.toml`**:
- Update the `target-version` list in the `[tool.black]` section to remove the old version
Expand All @@ -312,7 +312,7 @@ When dropping support for an old Python version (e.g., removing Python 3.10), up
**CI/CD configuration files:**
1. **`.github/workflows/build.yml`**:
- Remove the old version from the `unit-tests` and `integration-tests` job matrices
- Update the cache key version (e.g., increment `v28` to `v29`) to invalidate old caches
- Update the cache key version (e.g., increment `v34` to `v35`) to invalidate old caches

**Documentation:**
- Update the README.md and any getting started documentation to reflect the new supported Python version range
Expand Down
12 changes: 5 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
FROM python:3.14-slim
FROM python:3.15-slim
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections

RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install --no-install-recommends -y \
gcc \
procps \
python3-dev \
python3-setuptools \
python3-pip \
&& rm -rf /var/lib/apt/lists/*

RUN pip3 install --upgrade pip

WORKDIR /synapsePythonClient
COPY . .

RUN pip install --no-cache-dir .[pandas,curator]
# pip is not needed at runtime and its vendored dependencies trigger CVE findings
RUN pip install --no-cache-dir .[pandas,curator] \
&& pip uninstall -y pip


LABEL org.opencontainers.image.source='https://github.com/Sage-Bionetworks/synapsePythonClient'
Loading
Loading