You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Linting: ruff, black (line-length 88), isort (profile=black), bandit, flake8
14
+
- Linting: ruff, black (line-length 88), isort (profile=black), bandit
15
15
- CI: GitHub Actions → SonarCloud, PyPI deploy on release
16
+
- Docker: `Dockerfile` at repo root, published to `ghcr.io/sage-bionetworks/synapsepythonclient`
16
17
17
18
## Commands
18
19
@@ -94,8 +95,8 @@ Data flow: User → `operations/` factory → model async methods → `api/` ser
94
95
## Constraints
95
96
96
97
- 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.
97
-
-Do not write synchronous test files — write async tests only. The `@async_to_sync` decorator is validated by a dedicated smoke test. Duplicate sync tests were removed to cut CI cost.
98
-
-Unit tests must not make network calls — `pytest-socket` blocks all sockets. Use `pytest-mock` for HTTP mocking.
98
+
-For new tests, prefer async test modules. Existing synchronous unit tests under `tests/unit/` are retained and maintained; the `@async_to_sync` decorator is covered by a dedicated smoke test, so avoid adding duplicate sync/async test coverage.
99
+
-On non-Windows platforms, unit tests must not make external network calls — `pytest-socket` blocks internet-facing sockets while allowing Unix domain sockets. Socket blocking is skipped on Windows. Use `pytest-mock` for HTTP mocking.
99
100
-`develop` is the default/main branch, not `main` or `master`. PRs target `develop`.
100
101
- Legacy classes in root `synapseclient/` (entity.py, table.py, etc.) are kept for backwards compatibility. New features go in `models/` using the dataclass pattern.
101
102
- Avoid adding new methods to `client.py` (9600+ lines) — prefer the `api/` + `models/` layered pattern.
@@ -108,3 +109,9 @@ Data flow: User → `operations/` factory → model async methods → `api/` ser
108
109
- Unit test client fixture: session-scoped, `skip_checks=True`, `cache_client=False`
109
110
- Integration tests use `--reruns 3` for flaky retries and `-n 8 --dist loadscope` for parallelism
110
111
- Integration fixtures create per-worker Synapse projects; use `schedule_for_cleanup()` for teardown
- CI runs integration tests only on Python 3.10 and 3.14 (oldest + newest) to limit Synapse server load
114
+
115
+
## Maintenance
116
+
117
+
Each CLAUDE.md file has a `<!-- Last reviewed: YYYY-MM -->` header. Update this when the file is reviewed or modified. If a code change invalidates guidance in a CLAUDE.md file, update the guidance in the same PR.
Copy file name to clipboardExpand all lines: docs/CLAUDE.md
+9-1Lines changed: 9 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,9 @@ User-facing documentation for the Synapse Python Client. Built with MkDocs + Mat
8
8
9
9
MkDocs with Material theme, mkdocstrings (Google-style docstrings), termynal (CLI animations), markdown-include (file embedding).
10
10
11
+
### Python style
12
+
- Use built-in generics (`list`, `dict`, `tuple`, `set`) instead of `typing.List`, `typing.Dict`, etc. (Python 3.9+)
13
+
11
14
## Conventions
12
15
13
16
### Content types (Diataxis framework)
@@ -50,4 +53,9 @@ Defined in `mkdocs.yml` nav section. 5 main sections: Home, Tutorials, How-To Gu
50
53
- Do not edit tutorial code inline in markdown — edit the `.py` script file in `tutorial_scripts/` and update line ranges if needed.
51
54
- Reference docs auto-generate from source docstrings — to change method documentation, edit the docstring in the Python source, not the markdown.
52
55
-`mkdocs.yml` is at the repo root, not in `docs/` — it configures the entire doc build.
53
-
- Docs deploy via `mkdocs gh-deploy --force` targeting the `master` branch (not `develop`).
56
+
- Docs deploy to Read the Docs (configured via `.readthedocs.yaml` at repo root).
57
+
- Local build output goes to `docs_site/` (via `site_dir` in `mkdocs.yml`) — gitignored.
58
+
- Cross-referencing uses the `autorefs` plugin: `[display text][synapseclient.ClassName.method]` auto-resolves to mkdocstrings anchors.
59
+
60
+
### news.md
61
+
Release notes live in `docs/news.md`. Each release gets a heading with the version number and date, followed by bullet points describing changes. Group entries by category (Features, Bug Fixes, etc.). Reference Jira ticket numbers (SYNPY-XXXX) in each entry.
Copy file name to clipboardExpand all lines: synapseclient/api/CLAUDE.md
+39-4Lines changed: 39 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,33 +16,68 @@ async def verb_resource(
16
16
) -> Dict[str, Any]:
17
17
```
18
18
- All functions are `async def`
19
+
-`synapse_client` is **always**`Optional["Synapse"] = None` — never make it required. Callers omit it to use the cached singleton returned by `Synapse.get_client()`.
19
20
-`synapse_client` is always the last parameter, keyword-only (after `*`)
20
21
- Use `Synapse.get_client(synapse_client=synapse_client)` to get the client instance
21
22
- Use `TYPE_CHECKING` guard for `Synapse` import — avoids circular dependencies between `api/` and `client.py`
23
+
- Construct a `query_params` dictionary for non-null optional args, and pass it to the `params` arg of the REST call. See `entity_services.py` for the pattern.
24
+
25
+
### Docstring conventions
26
+
Module-level — every file opens with boilerplate linking to the Synapse REST controller:
27
+
```python
28
+
"""This module is responsible for exposing the services defined at:
Available methods: `rest_get_async`, `rest_post_async`, `rest_put_async`, `rest_delete_async`. Pass `endpoint=client.fileHandleEndpoint` for file handle operations; omit for the default repository endpoint. Use `json.dumps()` for request bodies — not raw dicts.
58
+
Available methods: `rest_get_async`, `rest_post_async`, `rest_put_async`, `rest_delete_async`. Pass `endpoint=client.fileHandleEndpoint` for file handle operations; omit for the default repository endpoint. Use `json.dumps()` for request bodies — not raw dicts. Always assign the response to a named `response` variable before returning or extracting attributes from it.
29
59
30
60
### Return values
31
61
- Most functions return raw `Dict[str, Any]` — transformation happens in the model layer via `fill_from_dict()`
32
62
- Some return typed dataclass instances (e.g., `EntityHeader` from `entity_services.py`) when the data is only used internally
33
63
- Delete operations return `None`
34
64
35
65
### Pagination
36
-
Use helpers from `api_client.py`:
66
+
Use async pagination helpers when the API endpoint returns a list of results. For single-object responses, a simple `return` is sufficient.
67
+
68
+
Helpers from `api_client.py`:
37
69
-`rest_get_paginated_async()` — for GET endpoints with limit/offset. Expects `results` or `children` key in response.
38
70
-`rest_post_paginated_async()` — for POST endpoints with `nextPageToken`. Expects `page` array in response.
39
-
Both are async generators yielding individual items.
71
+
Both are async generators yielding individual items. Reference `entity_services.py`, `table_services.py`, or `evaluation_services.py` for pagination patterns.
40
72
41
73
### Entity factory (`entity_factory.py`)
42
74
Polymorphic entity deserialization via concrete type dispatch. Maps Java class names from `core/constants/concrete_types.py` to model classes. When adding a new entity type, register the type mapping here.
43
75
76
+
### When to add a new service file vs. update an existing one
77
+
Add a new file when the Synapse REST controller is different (each file maps to one controller). Update an existing file when adding endpoints under the same controller.
78
+
44
79
### Adding a new service file
45
80
1. Create `synapseclient/api/new_service.py`
46
81
2. Add all public functions to `api/__init__.py` imports and `__all__` — every public function must be re-exported
47
82
3. Use `json.dumps()` for request bodies (not dict)
48
-
4. Reference `entity_services.py` for CRUD pattern, `table_services.py` for pagination pattern
83
+
4. Reference `entity_services.py` for CRUD pattern, `table_services.py`or `evaluation_services.py`for pagination pattern
Maps Java class names from Synapse REST API for polymorphic deserialization. When adding a new entity type, add its concrete type string here AND in `api/entity_factory.py` type map AND in `models/mixins/asynchronous_job.py` ASYNC_JOB_URIS if it's an async job type.
Copy file name to clipboardExpand all lines: synapseclient/core/download/CLAUDE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ File download from Synapse storage with MD5 validation, collision handling, and
7
7
## Conventions
8
8
9
9
### Primary download path
10
-
`download_async.py` is the primary async download implementation. `download_functions.py` contains shared helpers and the sync download wrapper.
10
+
`download_async.py` is the primary async download implementation. `download_functions.py` contains shared helpers and the sync download wrapper. The default part size of 8 MiB was empirically optimized for Synapse download throughput — do not change it without benchmarking.
11
11
12
12
### MD5 validation
13
13
Post-transfer MD5 validation is mandatory. Raises `SynapseMd5MismatchError` on mismatch — the download is retried automatically (60 retries spanning ~30 minutes).
Largest file in the codebase. Contains `DataModelParser`, `DataModelComponent`, `DataModelRelationships` classes. Uses networkx (DiGraph, MultiDiGraph) for node/edge relationships and cycle detection (via multiprocessing). Many deprecated validation rule enums marked for removal (SYNPY-1724, SYNPY-1692). Active development area — multiple recent PRs modifying conditionals, display names, and grouping.
15
-
16
-
### schema_registry.py
17
-
Query engine for the schema registry table. Default table ID: `syn69735275` (configurable via parameter). Builds SQL WHERE clauses from filter kwargs — supports exact match and LIKE pattern match. `return_latest_only=True` returns newest version URI only.
13
+
### schema_generation.py
14
+
Largest file in the codebase. Uses networkx (DiGraph, MultiDiGraph) for node/edge relationships and cycle detection (via multiprocessing). Many deprecated validation rule enums marked for removal (SYNPY-1724, SYNPY-1692). Active development area.
18
15
19
16
### schema_management.py
20
-
Thin wrappers around `JSONSchema` OOP model:
21
-
-`register_jsonschema()` / `register_jsonschema_async()` — loads schema from file, calls `.store_async()`
22
-
-`bind_jsonschema()` / `bind_jsonschema_async()` — binds schema to entity
23
-
-`fix_schema_name()` — replaces dashes/underscores with periods for Synapse compliance
24
-
25
-
Uses `wrap_async_to_sync()` for sync versions (not class decorator).
26
-
27
-
### file_based_metadata_task.py
28
-
Creates EntityView from JSON Schema bound to folder/project. `create_json_schema_entity_view()` auto-reorders columns (createdBy→name→id to front). `create_or_update_wiki_with_entity_view()` embeds EntityView query in Wiki page.
Uses `wrap_async_to_sync()` for sync versions (not class decorator). `fix_schema_name()` replaces dashes/underscores with periods for Synapse compliance.
32
18
33
19
### utils.py
34
-
`project_id_from_entity_id()` — traverses folder hierarchy up to project (max 1000 iterations). Uses legacy sync `get()` API in a loop — known tech debt.
20
+
`project_id_from_entity_id()` — traverses folder hierarchy up to project (max 1000 iterations). Uses `operations.get` in a loop — known tech debt.
0 commit comments