skills/dnanexus-integration/references/python-sdk.md
dxpy)This reference was verified against dxpy==0.410.0 (released 2026-07-14).
PyPI declares Python 3.8 or newer. This repository recommends Python 3.11+.
For a project:
uv add "dxpy==0.410.0"
For an isolated script:
uv run --with "dxpy==0.410.0" "script.py"
Inspect the installed API without authenticating. From this skill's root:
uv run --with "dxpy==0.410.0" \
"scripts/inspect_dxpy.py" --strict
dxpy uses the same configuration sources as dx. Prefer dx login for
interactive work and named secret injection for automation.
Never print:
DX_SECURITY_CONTEXTdxpy.SECURITY_CONTEXTSee authentication.md for configuration precedence and safe diagnosis.
Use handlers for objects and executions:
| Class | Purpose |
|---|---|
DXFile | File object |
DXRecord | Structured metadata record |
DXApplet | Project-local executable |
DXApp | Versioned app |
DXWorkflow | Native workflow |
DXJob | App/applet execution |
DXAnalysis | Workflow execution |
DXProject | Project/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.
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.
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.
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.
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.
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.410.0. Use
dxpy.open_dxfile().
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.
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:
name_mode defaults to exact.tags means all specified tags.tag is deprecated.describe=True returns full default descriptions; a field mapping is safer.limit means dxpy keeps paging until all matching results are read.Timestamps accept:
"-2d" or "-1w"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 analysesfind_jobs() for jobs onlyfind_analyses() for analyses onlyfind_executions() supports classname="job" or
classname="analysis".
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.
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.
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.410.0 include:
project, folder, nametags, properties, detailsinstance_type, stage_instance_typesstage_folders, rerun_stagesdepends_onpriority, head_job_on_demandignore_reuse, ignore_reuse_stagescost_limitmax_tree_spot_wait_time, max_job_spot_wait_timepreserve_job_outputsdetailed_job_metricssystem_requirementsBillable runs and reuse changes need explicit review.
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.410.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",
)
Current public dxpy exception classes include:
DXError: base SDK errorDXAPIError: non-200 API responseDXJobFailureError: job/analysis wait detected failure, termination, or
local timeout; re-describe remote state to distinguish themDXSearchError: invalid or failed searchDXFileError: file operation failureDXChecksumMismatchError: transfer integrity failureAPI error names such as ResourceNotFound, PermissionDenied, and
InvalidInput are usually represented by DXAPIError.name; they are not
top-level dxpy exception classes in 0.410.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.
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. Low-level methods expose API pagination, request shapes, and destructive options directly.
Never construct arbitrary API method names, hosts, or request bodies from untrusted input.
scripts/inspect_dxpy.py after upgrades.