Skill Tracer
Skills › Scientific Agent Skills › dnanexus-integration › python-sdk.md

python-sdk.md

561 lines · skills/dnanexus-integration/references/python-sdk.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Python SDK (`dxpy`)
2

Python SDK (dxpy)

Raw lines 1 to 2
3## Quick Navigation
4
5- [Baseline and authentication](#baseline)
6- [Handlers and links](#handler-model)
7- [Describe and files](#describe)
8- [Search](#search)
9- [Projects and records](#projects-and-folders)
10- [Run executables](#run-executables)
11- [Wait and output](#wait-and-output)
12- [Exceptions](#exceptions)
13- [Low-level API bindings](#low-level-api-bindings)
14- [Reliability checklist](#reliability-checklist)

Quick Navigation

  • Baseline and authentication
  • Handlers and links
  • Describe and files
  • Search
  • Projects and records
  • Run executables
  • Wait and output
  • Exceptions
  • Low-level API bindings
  • Reliability checklist
Raw lines 3 to 15
16## Baseline
18This reference was verified against `dxpy==0.415.0` (released 2026-09-22).
19PyPI declares Python 3.8 or newer. This repository recommends Python 3.11+.
21For a project:
23```bash
24uv add "dxpy==0.415.0"
25```
27For an isolated script:
29```bash
30uv run --with "dxpy==0.415.0" "script.py"
31```
33Inspect the installed API without authenticating. From this skill's root:
35```bash
36uv run --with "dxpy==0.415.0" \
37 "scripts/inspect_dxpy.py" --strict
38```

Baseline

This reference was verified against dxpy==0.415.0 (released 2026-09-22). PyPI declares Python 3.8 or newer. This repository recommends Python 3.11+.

For a project:

uv add "dxpy==0.415.0"

For an isolated script:

uv run --with "dxpy==0.415.0" "script.py"

Inspect the installed API without authenticating. From this skill's root:

uv run --with "dxpy==0.415.0" \
  "scripts/inspect_dxpy.py" --strict
Raw lines 16 to 39
Runs code scripts/inspect_dxpy.py :37
40## Authentication
42`dxpy` uses the same configuration sources as `dx`. Prefer `dx login` for
43interactive work and named secret injection for automation.
45Never print:
47- `DX_SECURITY_CONTEXT`
48- API token values
49- `dxpy.SECURITY_CONTEXT`
50- full process environments
52See `authentication.md` for configuration precedence and safe diagnosis.

Authentication

dxpy uses the same configuration sources as dx. Prefer dx login for interactive work and named secret injection for automation.

Never print:

  • DX_SECURITY_CONTEXT
  • API token values
  • dxpy.SECURITY_CONTEXT
  • full process environments

See authentication.md for configuration precedence and safe diagnosis.

Raw lines 40 to 53
54## Handler Model
56Use handlers for objects and executions:
58| Class | Purpose |
59|---|---|
60| `DXFile` | File object |
61| `DXRecord` | Structured metadata record |
62| `DXApplet` | Project-local executable |
63| `DXApp` | Versioned app |
64| `DXWorkflow` | Native workflow |
65| `DXJob` | App/applet execution |
66| `DXAnalysis` | Workflow execution |
67| `DXProject` | Project/data container |
69Create handlers from IDs:
71```python
72import dxpy
74file_obj = dxpy.DXFile("file-xxxx", project="project-xxxx")
75applet = dxpy.DXApplet("applet-xxxx", project="project-xxxx")
76app = dxpy.DXApp("app-xxxx")
77workflow = dxpy.DXWorkflow("workflow-xxxx", project="project-xxxx")
78job = dxpy.DXJob("job-xxxx")
79analysis = dxpy.DXAnalysis("analysis-xxxx")
80project = dxpy.DXProject("project-xxxx")
81```
83For an ID or link whose class is not known:
85```python
86handler = dxpy.get_handler(id_or_link, project="project-xxxx")
87```
89`get_handler()` accepts a string ID or a mapping containing a DNAnexus link.

Handler Model

Use handlers for objects and executions:

ClassPurpose
DXFileFile object
DXRecordStructured metadata record
DXAppletProject-local executable
DXAppVersioned app
DXWorkflowNative workflow
DXJobApp/applet execution
DXAnalysisWorkflow execution
DXProjectProject/data container

Create handlers from IDs:

import dxpy

file_obj = dxpy.DXFile("file-xxxx", project="project-xxxx")
applet = dxpy.DXApplet("applet-xxxx", project="project-xxxx")
app = dxpy.DXApp("app-xxxx")
workflow = dxpy.DXWorkflow("workflow-xxxx", project="project-xxxx")
job = dxpy.DXJob("job-xxxx")
analysis = dxpy.DXAnalysis("analysis-xxxx")
project = dxpy.DXProject("project-xxxx")

For an ID or link whose class is not known:

handler = dxpy.get_handler(id_or_link, project="project-xxxx")

get_handler() accepts a string ID or a mapping containing a DNAnexus link.

Raw lines 54 to 90
91## Links
93Create a data-object link:
95```python
96link = dxpy.dxlink("file-xxxx")
97```
99Include project context for a project-qualified link:
101```python
102link = dxpy.dxlink("file-xxxx", "project-xxxx")
103```
105Create a job-based output reference:
107```python
108output_reference = job.get_output_ref("aligned_bam")
109```
111Do not wrap a job output reference with `dxpy.dxlink()`. It is already a valid
112reference.

Links

Create a data-object link:

link = dxpy.dxlink("file-xxxx")

Include project context for a project-qualified link:

link = dxpy.dxlink("file-xxxx", "project-xxxx")

Create a job-based output reference:

output_reference = job.get_output_ref("aligned_bam")

Do not wrap a job output reference with dxpy.dxlink(). It is already a valid reference.

Raw lines 91 to 113
114## Describe
116```python
117import dxpy
119description = dxpy.describe(
120 "file-xxxx",
121 fields={"name", "size", "state", "archivalState"},
123```
125Handler:
127```python
128description = dxpy.DXFile(
129 "file-xxxx",
130 project="project-xxxx",
131).describe(
132 fields={"name", "size", "state"}
134```
136Request only needed fields. Object descriptions can contain PHI or internal
137metadata.

Describe

import dxpy

description = dxpy.describe(
    "file-xxxx",
    fields={"name", "size", "state", "archivalState"},
)

Handler:

description = dxpy.DXFile(
    "file-xxxx",
    project="project-xxxx",
).describe(
    fields={"name", "size", "state"}
)

Request only needed fields. Object descriptions can contain PHI or internal metadata.

Raw lines 114 to 138
141### Upload
143Verified signature:
145```text
146upload_local_file(
147 filename=None,
148 file=None,
149 media_type=None,
150 keep_open=False,
151 wait_on_close=False,
152 use_existing_dxfile=None,
153 show_progress=False,
154 write_buffer_size=None,
155 multithread=True,
156 **kwargs
158```
160Example:
162```python
163remote_file = dxpy.upload_local_file(
164 "results.vcf.gz",
165 project="project-xxxx",
166 folder="/results",
167 properties={"sample_id": "S001"},
168 tags=["validated"],
169 wait_on_close=True,
171```
173Exactly one of `filename` or `file` is required. `wait_on_close=True` is useful
174when the next operation requires a closed object.

Upload

Verified signature:

upload_local_file(
    filename=None,
    file=None,
    media_type=None,
    keep_open=False,
    wait_on_close=False,
    use_existing_dxfile=None,
    show_progress=False,
    write_buffer_size=None,
    multithread=True,
    **kwargs
)

Example:

remote_file = dxpy.upload_local_file(
    "results.vcf.gz",
    project="project-xxxx",
    folder="/results",
    properties={"sample_id": "S001"},
    tags=["validated"],
    wait_on_close=True,
)

Exactly one of filename or file is required. wait_on_close=True is useful when the next operation requires a closed object.

Raw lines 141 to 175
176### Download
178```python
179dxpy.download_dxfile(
180 "file-xxxx",
181 "results.vcf.gz",
182 project="project-xxxx",
183 show_progress=True,
185```
187`project` can affect which project/billing context is used for the download.

Download

dxpy.download_dxfile(
    "file-xxxx",
    "results.vcf.gz",
    project="project-xxxx",
    show_progress=True,
)

project can affect which project/billing context is used for the download.

Raw lines 176 to 188
189### Stream
191```python
192with dxpy.open_dxfile(
193 "file-xxxx",
194 project="project-xxxx",
195) as remote_stream:
196 prefix = remote_stream.read(4096)
197```
199`DXFile.open_file()` does not exist in dxpy 0.415.0. Use
200`dxpy.open_dxfile()`.

Stream

with dxpy.open_dxfile(
    "file-xxxx",
    project="project-xxxx",
) as remote_stream:
    prefix = remote_stream.read(4096)

DXFile.open_file() does not exist in dxpy 0.415.0. Use dxpy.open_dxfile().

Raw lines 189 to 201
202### Upload a string
204```python
205report = dxpy.upload_string(
206 '{"status":"ok"}\n',
207 project="project-xxxx",
208 folder="/reports",
209 name="status.json",
210 media_type="application/json",
211 wait_on_close=True,
213```
215Do not use this for credentials or sensitive diagnostic dumps.

Upload a string

report = dxpy.upload_string(
    '{"status":"ok"}\n',
    project="project-xxxx",
    folder="/reports",
    name="status.json",
    media_type="application/json",
    wait_on_close=True,
)

Do not use this for credentials or sensitive diagnostic dumps.

Raw lines 202 to 216
219### Data objects
221Verified signature includes:
223```text
224find_data_objects(
225 classname=None,
226 state=None,
227 visibility=None,
228 name=None,
229 name_mode="exact",
230 properties=None,
231 typename=None,
232 tags=None,
233 project=None,
234 folder=None,
235 recurse=None,
236 describe=False,
237 limit=None,
238 region=None,
239 archival_state=None,
240 return_handler=False,
241 ...
243```
245Example:
247```python
248results = dxpy.find_data_objects(
249 classname="file",
250 project="project-xxxx",
251 folder="/results",
252 recurse=True,
253 name="*.vcf.gz",
254 name_mode="glob",
255 state="closed",
256 describe={
257 "fields": {
258 "name": True,
259 "size": True,
260 "archivalState": True,
261 }
262 },
263 limit=200,
266for result in results:
267 description = result["describe"]
268 print(result["id"], description["name"])
269```
271Important:
273- Searches return generators.
274- `name_mode` defaults to exact.
275- `tags` means all specified tags.
276- `tag` is deprecated.
277- `describe=True` returns full default descriptions; a field mapping is safer.
278- Omitted `limit` means dxpy keeps paging until all matching results are read.
279- In dxpy 0.415.0, an explicit `limit` is also sent as the first API page size;
280 keep it at most 1000. To consume a larger bounded result set, use
281 `itertools.islice(dxpy.find_data_objects(...), total_limit)` with the SDK
282 `limit` omitted, so its internal page sizes remain valid.
283- Scope broad searches by project/folder/time.
285Timestamps accept:
287- Epoch milliseconds
288- Negative milliseconds relative to now
289- Relative strings such as `"-2d"` or `"-1w"`

Data objects

Verified signature includes:

find_data_objects(
    classname=None,
    state=None,
    visibility=None,
    name=None,
    name_mode="exact",
    properties=None,
    typename=None,
    tags=None,
    project=None,
    folder=None,
    recurse=None,
    describe=False,
    limit=None,
    region=None,
    archival_state=None,
    return_handler=False,
    ...
)

Example:

results = dxpy.find_data_objects(
    classname="file",
    project="project-xxxx",
    folder="/results",
    recurse=True,
    name="*.vcf.gz",
    name_mode="glob",
    state="closed",
    describe={
        "fields": {
            "name": True,
            "size": True,
            "archivalState": True,
        }
    },
    limit=200,
)

for result in results:
    description = result["describe"]
    print(result["id"], description["name"])

Important:

  • Searches return generators.
  • name_mode defaults to exact.
  • tags means all specified tags.
  • tag is deprecated.
  • describe=True returns full default descriptions; a field mapping is safer.
  • Omitted limit means dxpy keeps paging until all matching results are read.
  • In dxpy 0.415.0, an explicit limit is also sent as the first API page size; keep it at most 1000. To consume a larger bounded result set, use itertools.islice(dxpy.find_data_objects(...), total_limit) with the SDK limit omitted, so its internal page sizes remain valid.
  • Scope broad searches by project/folder/time.

Timestamps accept:

  • Epoch milliseconds
  • Negative milliseconds relative to now
  • Relative strings such as "-2d" or "-1w"
Raw lines 219 to 290
291### Executions
293```python
294executions = dxpy.find_executions(
295 project="project-xxxx",
296 state="failed",
297 created_after="-2d",
298 include_subjobs=True,
299 include_restarted=True,
300 describe={"fields": {"name": True, "failureReason": True}},
301 limit=100,
303```
305Use:
307- `find_executions()` for jobs and analyses
308- `find_jobs()` for jobs only
309- `find_analyses()` for analyses only
311`find_executions()` supports `classname="job"` or
312`classname="analysis"`.

Executions

executions = dxpy.find_executions(
    project="project-xxxx",
    state="failed",
    created_after="-2d",
    include_subjobs=True,
    include_restarted=True,
    describe={"fields": {"name": True, "failureReason": True}},
    limit=100,
)

Use:

  • find_executions() for jobs and analyses
  • find_jobs() for jobs only
  • find_analyses() for analyses only

find_executions() supports classname="job" or classname="analysis".

Raw lines 291 to 313
314## Projects and Folders
316```python
317project = dxpy.DXProject("project-xxxx")
319project.new_folder(
320 "/analysis/run-001/results",
321 parents=True,
324contents = project.list_folder(
325 "/analysis/run-001",
326 describe={"fields": {"name": True, "state": True}},
328```
330Move exact IDs:
332```python
333project.move(
334 "/analysis/run-001/final",
335 objects=["file-xxxx", "record-yyyy"],
337```
339Clone:
341```python
342source = dxpy.DXFile("file-xxxx", project="project-source")
343cloned = source.clone(
344 project="project-destination",
345 folder="/imports",
347```
349Delete only after explicit confirmation:
351```python
352project.remove_objects(["file-xxxx"], force=False)
353```
355Do not use `force=True` merely to hide a target-resolution error.

Projects and Folders

project = dxpy.DXProject("project-xxxx")

project.new_folder(
    "/analysis/run-001/results",
    parents=True,
)

contents = project.list_folder(
    "/analysis/run-001",
    describe={"fields": {"name": True, "state": True}},
)

Move exact IDs:

project.move(
    "/analysis/run-001/final",
    objects=["file-xxxx", "record-yyyy"],
)

Clone:

source = dxpy.DXFile("file-xxxx", project="project-source")
cloned = source.clone(
    project="project-destination",
    folder="/imports",
)

Delete only after explicit confirmation:

project.remove_objects(["file-xxxx"], force=False)

Do not use force=True merely to hide a target-resolution error.

Raw lines 314 to 356
Involves a person after explicit confirmation :349
357## Records
359```python
360record = dxpy.new_dxrecord(
361 project="project-xxxx",
362 folder="/metadata",
363 name="run-001",
364 types=["RunMetadata"],
365 details={
366 "pipeline": "rna-seq",
367 "version": "2.4.1",
368 },
369 close=True,
371```
373For an open mutable record:
375```python
376record = dxpy.DXRecord("record-xxxx", project="project-xxxx")
377details = record.get_details()
378details["status"] = "done"
379record.set_details(details)
380record.close()
381```
383`set_details()` replaces the details mapping supplied to the call. Avoid
384read-modify-write races on shared open records.

Records

record = dxpy.new_dxrecord(
    project="project-xxxx",
    folder="/metadata",
    name="run-001",
    types=["RunMetadata"],
    details={
        "pipeline": "rna-seq",
        "version": "2.4.1",
    },
    close=True,
)

For an open mutable record:

record = dxpy.DXRecord("record-xxxx", project="project-xxxx")
details = record.get_details()
details["status"] = "done"
record.set_details(details)
record.close()

set_details() replaces the details mapping supplied to the call. Avoid read-modify-write races on shared open records.

Raw lines 357 to 385
386## Run Executables
388App/applet runs return `DXJob`:
390```python
391job = dxpy.DXApplet(
392 "applet-xxxx",
393 project="project-xxxx",
394).run(
395 {"reads": dxpy.dxlink("file-xxxx")},
396 project="project-xxxx",
397 folder="/runs/run-001",
398 name="S001 alignment",
399 tags=["alignment"],
400 properties={"sample_id": "S001"},
401 priority="normal",
402 cost_limit=25,
404```
406Workflow runs return `DXAnalysis`:
408```python
409analysis = dxpy.DXWorkflow(
410 "workflow-xxxx",
411 project="project-xxxx",
412).run(
413 {
414 "0.reads": dxpy.dxlink(
415 "file-xxxx",
416 "project-xxxx",
417 )
418 },
419 project="project-xxxx",
420 folder="/runs/run-002",
421 cost_limit=50,
423```
425Useful run arguments in dxpy 0.415.0 include:
427- `project`, `folder`, `name`
428- `tags`, `properties`, `details`
429- `instance_type`, `stage_instance_types`
430- `stage_folders`, `rerun_stages`
431- `depends_on`
432- `priority`, `head_job_on_demand`
433- `ignore_reuse`, `ignore_reuse_stages`
434- `cost_limit`
435- `max_tree_spot_wait_time`, `max_job_spot_wait_time`
436- `preserve_job_outputs`
437- `detailed_job_metrics`
438- `system_requirements`
440Billable runs and reuse changes need explicit review.

Run Executables

App/applet runs return DXJob:

job = dxpy.DXApplet(
    "applet-xxxx",
    project="project-xxxx",
).run(
    {"reads": dxpy.dxlink("file-xxxx")},
    project="project-xxxx",
    folder="/runs/run-001",
    name="S001 alignment",
    tags=["alignment"],
    properties={"sample_id": "S001"},
    priority="normal",
    cost_limit=25,
)

Workflow runs return DXAnalysis:

analysis = dxpy.DXWorkflow(
    "workflow-xxxx",
    project="project-xxxx",
).run(
    {
        "0.reads": dxpy.dxlink(
            "file-xxxx",
            "project-xxxx",
        )
    },
    project="project-xxxx",
    folder="/runs/run-002",
    cost_limit=50,
)

Useful run arguments in dxpy 0.415.0 include:

  • project, folder, name
  • tags, properties, details
  • instance_type, stage_instance_types
  • stage_folders, rerun_stages
  • depends_on
  • priority, head_job_on_demand
  • ignore_reuse, ignore_reuse_stages
  • cost_limit
  • max_tree_spot_wait_time, max_job_spot_wait_time
  • preserve_job_outputs
  • detailed_job_metrics
  • system_requirements

Billable runs and reuse changes need explicit review.

Raw lines 386 to 441
442## Wait and Output
444```python
445from dxpy.exceptions import DXError, DXJobFailureError
447try:
448 job.wait_on_done(interval=5, timeout=3600)
449except DXJobFailureError as error:
450 status = job.describe(
451 fields={
452 "state": True,
453 "failureReason": True,
454 "failureMessage": True,
455 }
456 )
457 state = status.get("state")
458 if state not in {"failed", "terminated"}:
459 raise TimeoutError(
460 f"local wait ended while remote job state is {state!r}"
461 ) from error
462 reason = status.get("failureReason") or "Terminated"
463 message = status.get("failureMessage") or str(error)
464 raise RuntimeError(f"{reason}: {message}") from error
465except DXError as error:
466 raise RuntimeError(f"polling failed: {error}") from error
468outputs = job.describe(fields={"output": True})["output"]
469```
471Defaults for `DXJob.wait_on_done()` and `DXAnalysis.wait_on_done()` are a
472two-second poll interval and a seven-day timeout. Set an explicit timeout that
473matches the caller's needs. In dxpy 0.415.0, `DXJobFailureError` represents
474remote failure, termination, **or local wait timeout** despite the method
475docstring; re-describe state before classifying it.
477To chain jobs, avoid waiting:
479```python
480downstream = dxpy.DXApplet("applet-next").run(
481 {"input": job.get_output_ref("result")},
482 project="project-xxxx",
484```

Wait and Output

from dxpy.exceptions import DXError, DXJobFailureError

try:
    job.wait_on_done(interval=5, timeout=3600)
except DXJobFailureError as error:
    status = job.describe(
        fields={
            "state": True,
            "failureReason": True,
            "failureMessage": True,
        }
    )
    state = status.get("state")
    if state not in {"failed", "terminated"}:
        raise TimeoutError(
            f"local wait ended while remote job state is {state!r}"
        ) from error
    reason = status.get("failureReason") or "Terminated"
    message = status.get("failureMessage") or str(error)
    raise RuntimeError(f"{reason}: {message}") from error
except DXError as error:
    raise RuntimeError(f"polling failed: {error}") from error

outputs = job.describe(fields={"output": True})["output"]

Defaults for DXJob.wait_on_done() and DXAnalysis.wait_on_done() are a two-second poll interval and a seven-day timeout. Set an explicit timeout that matches the caller's needs. In dxpy 0.415.0, DXJobFailureError represents remote failure, termination, or local wait timeout despite the method docstring; re-describe state before classifying it.

To chain jobs, avoid waiting:

downstream = dxpy.DXApplet("applet-next").run(
    {"input": job.get_output_ref("result")},
    project="project-xxxx",
)
Raw lines 442 to 485
486## Exceptions
488Current public dxpy exception classes include:
490- `DXError`: base SDK error
491- `DXAPIError`: non-200 API response
492- `DXJobFailureError`: job/analysis wait detected failure, termination, or
493 local timeout; re-describe remote state to distinguish them
494- `DXSearchError`: invalid or failed search
495- `DXFileError`: file operation failure
496- `DXChecksumMismatchError`: transfer integrity failure
498API error names such as `ResourceNotFound`, `PermissionDenied`, and
499`InvalidInput` are usually represented by `DXAPIError.name`; they are not
500top-level dxpy exception classes in 0.415.0.
502```python
503from dxpy.exceptions import DXAPIError
505try:
506 description = dxpy.describe("file-xxxx")
507except DXAPIError as error:
508 print(f"DNAnexus API error: {error.name} (HTTP {error.code})")
509 raise
510```
512Do not dump `error.details` blindly; it can contain sensitive object metadata.

Exceptions

Current public dxpy exception classes include:

  • DXError: base SDK error
  • DXAPIError: non-200 API response
  • DXJobFailureError: job/analysis wait detected failure, termination, or local timeout; re-describe remote state to distinguish them
  • DXSearchError: invalid or failed search
  • DXFileError: file operation failure
  • DXChecksumMismatchError: transfer integrity failure

API error names such as ResourceNotFound, PermissionDenied, and InvalidInput are usually represented by DXAPIError.name; they are not top-level dxpy exception classes in 0.415.0.

from dxpy.exceptions import DXAPIError

try:
    description = dxpy.describe("file-xxxx")
except DXAPIError as error:
    print(f"DNAnexus API error: {error.name} (HTTP {error.code})")
    raise

Do not dump error.details blindly; it can contain sensitive object metadata.

Raw lines 486 to 513
514## Low-Level API Bindings
516Generated wrappers are available under `dxpy.api`, for example:
518```python
519response = dxpy.api.system_find_data_objects(
520 {
521 "class": "file",
522 "scope": {
523 "project": "project-xxxx",
524 "folder": "/results",
525 "recurse": True,
526 },
527 "name": {"glob": "*.bam"},
528 "limit": 100,
529 }
531```
533Prefer high-level search/handler methods unless a required field is unavailable
534there. For `/system/findDataObjects`, `response["results"]` is one page;
535`response["next"]` is a mapping or `null`. Pass a non-null cursor unchanged as
536`starting` in the next body. The API page `limit` is 1–1000; the high-level
537`find_data_objects(limit=...)` instead bounds the total generator output.
538It also uses that value for the initial page request, so the same 1000 ceiling
539applies to explicit SDK limits in this release.
541API methods use HTTPS POST to `https://api.dnanexus.com/<method>` with a JSON
542body and Bearer authorization. URL query parameters are ignored. The optional
543`DNAnexus-API: 1.0.0` header selects the documented API version; the SDK handles
544these details. Low-level methods expose destructive options directly.
546Never construct arbitrary API method names, hosts, or request bodies from
547untrusted input.

Low-Level API Bindings

Generated wrappers are available under dxpy.api, for example:

response = dxpy.api.system_find_data_objects(
    {
        "class": "file",
        "scope": {
            "project": "project-xxxx",
            "folder": "/results",
            "recurse": True,
        },
        "name": {"glob": "*.bam"},
        "limit": 100,
    }
)

Prefer high-level search/handler methods unless a required field is unavailable there. For /system/findDataObjects, response["results"] is one page; response["next"] is a mapping or null. Pass a non-null cursor unchanged as starting in the next body. The API page limit is 1–1000; the high-level find_data_objects(limit=...) instead bounds the total generator output. It also uses that value for the initial page request, so the same 1000 ceiling applies to explicit SDK limits in this release.

API methods use HTTPS POST to https://api.dnanexus.com/<method> with a JSON body and Bearer authorization. URL query parameters are ignored. The optional DNAnexus-API: 1.0.0 header selects the documented API version; the SDK handles these details. Low-level methods expose destructive options directly.

Never construct arbitrary API method names, hosts, or request bodies from untrusted input.

Raw lines 514 to 548
549## Reliability Checklist
551- Pin the tested dxpy version.
552- Pass explicit project/folder context.
553- Bound searches and polling.
554- Use links and output references, not hand-built mappings.
555- Wait for file closure when required.
556- Catch specific SDK exceptions.
557- Retry only documented transient failures with backoff.
558- Preserve request IDs from API errors for support, but redact object metadata.
559- Never log credentials or whole environments.
560- Require confirmation for billable/destructive operations.
561- Test SDK assumptions with `scripts/inspect_dxpy.py` after upgrades.

Reliability Checklist

  • Pin the tested dxpy version.
  • Pass explicit project/folder context.
  • Bound searches and polling.
  • Use links and output references, not hand-built mappings.
  • Wait for file closure when required.
  • Catch specific SDK exceptions.
  • Retry only documented transient failures with backoff.
  • Preserve request IDs from API errors for support, but redact object metadata.
  • Never log credentials or whole environments.
  • Require confirmation for billable/destructive operations.
  • Test SDK assumptions with scripts/inspect_dxpy.py after upgrades.
Raw lines 549 to 561
Runs code scripts/inspect_dxpy.py :561