dxpy 0.364.0__tar.gz → 0.366.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dxpy-0.364.0 → dxpy-0.366.0}/PKG-INFO +3 -3
- {dxpy-0.364.0 → dxpy-0.366.0}/Readme.md +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/__init__.py +41 -64
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxfile.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/search.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/cp.py +3 -4
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/dataset_utilities.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/exceptions.py +7 -8
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/executable_builder.py +1 -1
- dxpy-0.366.0/dxpy/nextflow/ImageRef.py +141 -0
- dxpy-0.366.0/dxpy/nextflow/ImageRefFactory.py +43 -0
- dxpy-0.366.0/dxpy/nextflow/collect_images.py +68 -0
- dxpy-0.366.0/dxpy/nextflow/nextaur_assets.staging.json +8 -0
- dxpy-0.366.0/dxpy/nextflow/nextflow_assets.staging.json +8 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/nextflow/nextflow_builder.py +3 -4
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/nextflow/nextflow_templates.py +17 -15
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/nextflow/nextflow_utils.py +5 -2
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/scripts/dx.py +36 -31
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/scripts/dx_app_wizard.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/scripts/dx_build_app.py +2 -2
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/scripts/dx_build_applet.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/basic/src/code.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/basic/test/test.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/parallelized/src/code.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/parallelized/test/test.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/scatter-process-gather/src/code.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/scatter-process-gather/test/test.py +1 -1
- dxpy-0.366.0/dxpy/toolkit_version.py +1 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/file_handle.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/pretty_print.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy.egg-info/PKG-INFO +3 -3
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy.egg-info/SOURCES.txt +5 -4
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy.egg-info/requires.txt +2 -24
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy.egg-info/top_level.txt +0 -1
- dxpy-0.366.0/requirements.txt +10 -0
- dxpy-0.366.0/requirements_setuptools.txt +3 -0
- dxpy-0.366.0/requirements_test.txt +9 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-clone-asset +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-download-all-inputs +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-fetch-bundled-depends +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-generate-dxapp +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-jobutil-add-output +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-jobutil-dxlink +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-jobutil-new-job +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-jobutil-parse-link +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-jobutil-report-error +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-log-stream +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-mount-all-inputs +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-print-bash-vars +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-upload-all-outputs +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/setup.py +3 -10
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_batch.py +10 -7
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_create_cohort.py +2 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_describe.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dx_app_wizard.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dx_bash_helpers.py +3 -15
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dx_completion.py +2 -2
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dx_symlink.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dxabs.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dxasset.py +2 -2
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dxclient.py +120 -100
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dxpy.py +25 -24
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dxpy_utils.py +3 -2
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dxunpack.py +2 -2
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_extract_assay.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_extract_dataset.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_extract_expression.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_extract_somatic.py +1 -1
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_nextflow.py +37 -12
- dxpy-0.366.0/test/test_nextflow_ImageRef.py +75 -0
- dxpy-0.366.0/test/test_nextflow_ImageRefFactory.py +67 -0
- dxpy-0.364.0/dxpy/nextflow/ImageRefParser.py +0 -108
- dxpy-0.364.0/dxpy/nextflow/nextaur_assets.staging.json +0 -8
- dxpy-0.364.0/dxpy/nextflow/nextflow_assets.staging.json +0 -8
- dxpy-0.364.0/dxpy/packages/__init__.py +0 -3
- dxpy-0.364.0/dxpy/toolkit_version.py +0 -1
- dxpy-0.364.0/requirements.txt +0 -15
- dxpy-0.364.0/requirements_backports.txt +0 -2
- dxpy-0.364.0/requirements_setuptools.txt +0 -3
- dxpy-0.364.0/requirements_test.txt +0 -14
- dxpy-0.364.0/test/test_nextflow_ImageRefParser.py +0 -86
- {dxpy-0.364.0 → dxpy-0.366.0}/MANIFEST.in +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/api.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/app_builder.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/app_categories.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/asset_builder.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/cmd_line_options_validator.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/data_transformations.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/dataset.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/json_validation_by_schema.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/schemas/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/schemas/assay_filtering_conditions.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/schemas/assay_filtering_json_schemas.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/schemas/input_arguments_validation_schemas.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/vizclient.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/vizserver_filters_from_json_parser.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/apollo/vizserver_payload_builder.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/auth.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/download_all_inputs.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxanalysis.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxapp.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxapp_container_functions.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxapplet.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxdatabase.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxdatabase_functions.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxdataobject_functions.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxfile_functions.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxglobalworkflow.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxjob.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxproject.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxrecord.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/dxworkflow.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/bindings/mount_all_inputs.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/download.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/exec_io.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/help_messages.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/org.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/output_handling.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/parsers.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/cli/workflow.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/compat.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest_staging.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest_staging_vep.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest_vep.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/cohort_filter_payload.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/column_conditions.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/column_conversion.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/filter_to_payload.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/input_validation.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/input_validation_somatic.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/retrieve_allele_schema.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/retrieve_annotation_schema.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/retrieve_bins.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/retrieve_genotype_schema.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/return_columns_allele.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/return_columns_annotation.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/return_columns_genotype.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dx_extract_utils/somatic_filter_payload.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/dxlog.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/nextflow/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/nextflow/nextaur_assets.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/nextflow/nextflow_assets.json +0 -0
- {dxpy-0.364.0/test/nextflow → dxpy-0.366.0/dxpy/packages}/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/scripts/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/ssh_tunnel_app_support.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/system_requirements.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/bash.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/python.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/Readme.md +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/bash/basic/dxapp.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/bash/basic/src/code.sh +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/bash/parallelized/dxapp.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/bash/parallelized/src/code.sh +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/bash/scatter-process-gather/dxapp.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/bash/scatter-process-gather/src/code.sh +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/nextflow/dxapp.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/nextflow/src/nextflow.sh +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/basic/dxapp.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/parallelized/dxapp.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/templates/python/scatter-process-gather/dxapp.json +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/templating/utils.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/__init__.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/batch_utils.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/completer.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/config.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/describe.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/exec_utils.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/executable_unbuilder.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/file_load_utils.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/genomic_utils.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/job_log_client.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/local_exec_utils.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/pathmatch.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/printing.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/resolver.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/utils/spelling_corrector.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy/workflow_builder.py +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy.egg-info/dependency_links.txt +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy.egg-info/entry_points.txt +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/dxpy.egg-info/not-zip-safe +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-docker +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/scripts/dx-notebook-reconnect +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/setup.cfg +0 -0
- {dxpy-0.364.0 → dxpy-0.366.0}/test/test_dx-docker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dxpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.366.0
|
|
4
4
|
Summary: DNAnexus Platform API bindings for Python
|
|
5
5
|
Home-page: https://github.com/dnanexus/dx-toolkit
|
|
6
6
|
Author: Aleksandra Zalcman, Andrey Kislyuk, Anurag Biyani, Geet Duggal, Katherine Lai, Kurt Jensen, Marek Hrvol, Ohad Rodeh, Phil Sung
|
|
@@ -14,7 +14,7 @@ Classifier: Operating System :: POSIX
|
|
|
14
14
|
Classifier: Programming Language :: Python
|
|
15
15
|
Classifier: Programming Language :: Unix Shell
|
|
16
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
-
Requires-Python: >=
|
|
17
|
+
Requires-Python: >=3.6
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
Provides-Extra: pandas
|
|
20
20
|
Provides-Extra: xattr
|
|
@@ -77,7 +77,7 @@ Other useful resources:
|
|
|
77
77
|
|
|
78
78
|
Python version compatibility
|
|
79
79
|
----------------------------
|
|
80
|
-
dxpy is supported on Python 3 (3.
|
|
80
|
+
dxpy is supported on Python 3 (3.6+)
|
|
81
81
|
|
|
82
82
|
Convention for Python scripts that are also modules
|
|
83
83
|
---------------------------------------------------
|
|
@@ -56,7 +56,7 @@ Other useful resources:
|
|
|
56
56
|
|
|
57
57
|
Python version compatibility
|
|
58
58
|
----------------------------
|
|
59
|
-
dxpy is supported on Python 3 (3.
|
|
59
|
+
dxpy is supported on Python 3 (3.6+)
|
|
60
60
|
|
|
61
61
|
Convention for Python scripts that are also modules
|
|
62
62
|
---------------------------------------------------
|
|
@@ -20,8 +20,7 @@ the following sources in order of decreasing priority:
|
|
|
20
20
|
|
|
21
21
|
1. Environment variables
|
|
22
22
|
2. Values stored in ``~/.dnanexus_config/environment``
|
|
23
|
-
3.
|
|
24
|
-
4. Hardcoded defaults
|
|
23
|
+
3. Hardcoded defaults
|
|
25
24
|
|
|
26
25
|
The bindings are configured by the following environment variables:
|
|
27
26
|
|
|
@@ -133,30 +132,19 @@ logger.addHandler(logging.NullHandler())
|
|
|
133
132
|
import os, sys, json, time, platform, ssl, traceback
|
|
134
133
|
import errno
|
|
135
134
|
import math
|
|
136
|
-
import mmap
|
|
137
|
-
import requests
|
|
138
135
|
import socket
|
|
139
136
|
import threading
|
|
140
|
-
import
|
|
141
|
-
|
|
137
|
+
import certifi
|
|
142
138
|
from collections import namedtuple
|
|
143
139
|
|
|
144
140
|
from . import exceptions
|
|
145
|
-
from .compat import
|
|
141
|
+
from .compat import BadStatusLine, StringIO, bytes, Repr
|
|
146
142
|
from .utils.printing import BOLD, BLUE, YELLOW, GREEN, RED, WHITE
|
|
147
143
|
|
|
148
144
|
from random import randint
|
|
149
|
-
|
|
150
|
-
from requests.packages import urllib3
|
|
145
|
+
import urllib3
|
|
151
146
|
from threading import Lock
|
|
152
|
-
from . import
|
|
153
|
-
|
|
154
|
-
try:
|
|
155
|
-
# python-3
|
|
156
|
-
from urllib.parse import urlsplit
|
|
157
|
-
except ImportError:
|
|
158
|
-
# python-2
|
|
159
|
-
from urlparse import urlsplit
|
|
147
|
+
from urllib.parse import urlsplit
|
|
160
148
|
|
|
161
149
|
sequence_number_mutex = threading.Lock()
|
|
162
150
|
counter = 0
|
|
@@ -172,7 +160,7 @@ def _get_sequence_number():
|
|
|
172
160
|
def configure_urllib3():
|
|
173
161
|
# Disable verbose urllib3 warnings and log messages
|
|
174
162
|
urllib3.disable_warnings(category=urllib3.exceptions.InsecurePlatformWarning)
|
|
175
|
-
logging.getLogger('
|
|
163
|
+
logging.getLogger('urllib3.connectionpool').setLevel(logging.ERROR)
|
|
176
164
|
|
|
177
165
|
configure_urllib3()
|
|
178
166
|
|
|
@@ -203,8 +191,13 @@ USER_AGENT = "{name}/{version} ({platform}) Python/{python_version}".format(name
|
|
|
203
191
|
version=TOOLKIT_VERSION,
|
|
204
192
|
platform=platform.platform(),
|
|
205
193
|
python_version=platform.python_version())
|
|
206
|
-
_default_certs =
|
|
207
|
-
_default_headers =
|
|
194
|
+
_default_certs = certifi.where()
|
|
195
|
+
_default_headers = {
|
|
196
|
+
"User-Agent": USER_AGENT,
|
|
197
|
+
"Accept-Encoding": "gzip, deflate",
|
|
198
|
+
"Accept": "*/*",
|
|
199
|
+
"Connection": "keep-alive",
|
|
200
|
+
}
|
|
208
201
|
_default_timeout = urllib3.util.timeout.Timeout(connect=DEFAULT_TIMEOUT, read=DEFAULT_TIMEOUT)
|
|
209
202
|
_RequestForAuth = namedtuple('_RequestForAuth', 'method url headers')
|
|
210
203
|
_expected_exceptions = (exceptions.network_exceptions, exceptions.DXAPIError, BadStatusLine, exceptions.BadJSONInReply,
|
|
@@ -277,7 +270,7 @@ def _get_pool_manager(verify, cert_file, key_file, ssl_context=None):
|
|
|
277
270
|
cert_file=cert_file,
|
|
278
271
|
key_file=key_file,
|
|
279
272
|
ssl_context=ssl_context,
|
|
280
|
-
ca_certs=verify or os.environ.get('DX_CA_CERT') or
|
|
273
|
+
ca_certs=verify or os.environ.get('DX_CA_CERT') or certifi.where())
|
|
281
274
|
if verify is False or os.environ.get('DX_CA_CERT') == 'NOVERIFY':
|
|
282
275
|
pool_args.update(cert_reqs=ssl.CERT_NONE, ca_certs=None)
|
|
283
276
|
urllib3.disable_warnings()
|
|
@@ -295,15 +288,7 @@ def _process_method_url_headers(method, url, headers):
|
|
|
295
288
|
_headers.update(headers)
|
|
296
289
|
else:
|
|
297
290
|
_url, _headers = url, headers
|
|
298
|
-
|
|
299
|
-
# *data*, which should not be done. Also, per HTTP/1.1 headers must be encoded with MIME, but we'll
|
|
300
|
-
# disregard that here, and just encode them with the Python default (ascii) and fail for any non-ascii
|
|
301
|
-
# content. See http://tools.ietf.org/html/rfc3987 for a discussion of encoding URLs.
|
|
302
|
-
# TODO: ascertain whether this is a problem in Python 3/make test
|
|
303
|
-
if USING_PYTHON2:
|
|
304
|
-
return method.encode(), _url.encode('utf-8'), {k.encode(): v.encode() for k, v in _headers.items()}
|
|
305
|
-
else:
|
|
306
|
-
return method, _url, _headers
|
|
291
|
+
return method, _url, _headers
|
|
307
292
|
|
|
308
293
|
|
|
309
294
|
# When any of the following errors are indicated, we are sure that the
|
|
@@ -316,9 +301,7 @@ _RETRYABLE_SOCKET_ERRORS = {
|
|
|
316
301
|
}
|
|
317
302
|
|
|
318
303
|
_RETRYABLE_WITH_RESPONSE = (exceptions.ContentLengthError, BadStatusLine, exceptions.BadJSONInReply,
|
|
319
|
-
|
|
320
|
-
if not USING_PYTHON2:
|
|
321
|
-
_RETRYABLE_WITH_RESPONSE += (ConnectionResetError,)
|
|
304
|
+
ConnectionResetError, urllib3.exceptions.ProtocolError, exceptions.UrllibInternalError)
|
|
322
305
|
|
|
323
306
|
def _is_retryable_exception(e):
|
|
324
307
|
"""Returns True if the exception is always safe to retry.
|
|
@@ -333,7 +316,7 @@ def _is_retryable_exception(e):
|
|
|
333
316
|
"""
|
|
334
317
|
if isinstance(e, urllib3.exceptions.ProtocolError):
|
|
335
318
|
return True
|
|
336
|
-
if
|
|
319
|
+
if isinstance(e, ConnectionResetError):
|
|
337
320
|
return True
|
|
338
321
|
if isinstance(e, (socket.gaierror, socket.herror)):
|
|
339
322
|
return True
|
|
@@ -341,8 +324,6 @@ def _is_retryable_exception(e):
|
|
|
341
324
|
return True
|
|
342
325
|
if isinstance(e, urllib3.exceptions.NewConnectionError):
|
|
343
326
|
return True
|
|
344
|
-
if isinstance(e, requests.exceptions.SSLError):
|
|
345
|
-
return True
|
|
346
327
|
if isinstance(e, urllib3.exceptions.SSLError):
|
|
347
328
|
return True
|
|
348
329
|
if isinstance(e, ssl.SSLError):
|
|
@@ -496,13 +477,11 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
|
|
|
496
477
|
:type auth: tuple, object, True (default), or None
|
|
497
478
|
:param timeout: HTTP request timeout, in seconds
|
|
498
479
|
:type timeout: float
|
|
499
|
-
:param config: *config* value to pass through to :meth:`requests.request`
|
|
500
|
-
:type config: dict
|
|
501
480
|
:param use_compression: Deprecated
|
|
502
481
|
:type use_compression: string or None
|
|
503
482
|
:param jsonify_data: If True, *data* is converted from a Python list or dict to a JSON string
|
|
504
483
|
:type jsonify_data: boolean
|
|
505
|
-
:param want_full_response: If True, the full :class:`
|
|
484
|
+
:param want_full_response: If True, the full :class:`urllib3.response.HTTPResponse` object is returned (otherwise, only the content of the response body is returned)
|
|
506
485
|
:type want_full_response: boolean
|
|
507
486
|
:param decode_response_body: If True (and *want_full_response* is False), the response body is decoded and, if it is a JSON string, deserialized. Otherwise, the response body is uncompressed if transport compression is on, and returned raw.
|
|
508
487
|
:type decode_response_body: boolean
|
|
@@ -523,9 +502,9 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
|
|
|
523
502
|
|
|
524
503
|
:type always_retry: boolean
|
|
525
504
|
:returns: Response from API server in the format indicated by *want_full_response* and *decode_response_body*.
|
|
526
|
-
:raises: :exc:`exceptions.DXAPIError` or a subclass if the server returned a non-200 status code; :exc:`
|
|
505
|
+
:raises: :exc:`exceptions.DXAPIError` or a subclass if the server returned a non-200 status code; :exc:`urllib3.exceptions.HTTPError` if an invalid response was received from the server; or :exc:`urllib3.exceptions.ConnectionError` if a connection cannot be established.
|
|
527
506
|
|
|
528
|
-
Wrapper around :meth:`
|
|
507
|
+
Wrapper around :meth:`urllib3.request()` that makes an HTTP
|
|
529
508
|
request, inserting authentication headers and (by default)
|
|
530
509
|
converting *data* to JSON.
|
|
531
510
|
|
|
@@ -607,28 +586,26 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
|
|
|
607
586
|
return i
|
|
608
587
|
|
|
609
588
|
_headers = {ensure_ascii(k): ensure_ascii(v) for k, v in _headers.items()}
|
|
610
|
-
|
|
589
|
+
|
|
590
|
+
# This is needed for python 3 urllib
|
|
591
|
+
_headers.pop(b'host', None)
|
|
592
|
+
_headers.pop(b'content-length', None)
|
|
593
|
+
_headers.pop(b'Content-Length', None)
|
|
594
|
+
|
|
595
|
+
# The libraries downstream (http client) require elimination of non-ascii
|
|
596
|
+
# chars from URL.
|
|
597
|
+
# We check if the URL contains non-ascii characters to see if we need to
|
|
598
|
+
# quote it. It is important not to always quote the path (here: parts[2])
|
|
599
|
+
# since it might contain elements (e.g. HMAC for api proxy) containing
|
|
600
|
+
# special characters that should not be quoted.
|
|
601
|
+
try:
|
|
602
|
+
ensure_ascii(_url)
|
|
611
603
|
encoded_url = _url
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
# The libraries downstream (http client) require elimination of non-ascii
|
|
619
|
-
# chars from URL.
|
|
620
|
-
# We check if the URL contains non-ascii characters to see if we need to
|
|
621
|
-
# quote it. It is important not to always quote the path (here: parts[2])
|
|
622
|
-
# since it might contain elements (e.g. HMAC for api proxy) containing
|
|
623
|
-
# special characters that should not be quoted.
|
|
624
|
-
try:
|
|
625
|
-
ensure_ascii(_url)
|
|
626
|
-
encoded_url = _url
|
|
627
|
-
except UnicodeEncodeError:
|
|
628
|
-
import urllib.parse
|
|
629
|
-
parts = list(urllib.parse.urlparse(_url))
|
|
630
|
-
parts[2] = urllib.parse.quote(parts[2])
|
|
631
|
-
encoded_url = urllib.parse.urlunparse(parts)
|
|
604
|
+
except UnicodeEncodeError:
|
|
605
|
+
import urllib.parse
|
|
606
|
+
parts = list(urllib.parse.urlparse(_url))
|
|
607
|
+
parts[2] = urllib.parse.quote(parts[2])
|
|
608
|
+
encoded_url = urllib.parse.urlunparse(parts)
|
|
632
609
|
|
|
633
610
|
response = pool_manager.request(_method, encoded_url, headers=_headers, body=body,
|
|
634
611
|
timeout=timeout, retries=False, **kwargs)
|
|
@@ -750,7 +727,7 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
|
|
|
750
727
|
# The server has closed the connection prematurely
|
|
751
728
|
if (response is not None
|
|
752
729
|
and response.status == 400 and is_retryable and method == 'PUT'
|
|
753
|
-
and isinstance(e,
|
|
730
|
+
and isinstance(e, urllib3.exceptions.HTTPError)):
|
|
754
731
|
request_timeout_str = '<Code>RequestTimeout</Code>'
|
|
755
732
|
if (request_timeout_str in exception_msg
|
|
756
733
|
or (isinstance(e, exceptions.HTTPErrorWithContent) and request_timeout_str in e.content)):
|
|
@@ -810,7 +787,7 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
|
|
|
810
787
|
raise AssertionError('Should never reach this line: should never break out of loop')
|
|
811
788
|
|
|
812
789
|
|
|
813
|
-
class DXHTTPOAuth2(
|
|
790
|
+
class DXHTTPOAuth2():
|
|
814
791
|
def __init__(self, security_context):
|
|
815
792
|
self.security_context = security_context
|
|
816
793
|
|
|
@@ -658,7 +658,7 @@ class DXFile(DXDataObject):
|
|
|
658
658
|
:type display_progress: boolean
|
|
659
659
|
:param report_progress_fn: Optional: a function to call that takes in two arguments (self, # bytes transmitted)
|
|
660
660
|
:type report_progress_fn: function or None
|
|
661
|
-
:raises: :exc:`dxpy.exceptions.DXFileError` if *index* is given and is not in the correct range, :exc:`
|
|
661
|
+
:raises: :exc:`dxpy.exceptions.DXFileError` if *index* is given and is not in the correct range, :exc:`urllib3.exceptions.HTTPError` if upload fails
|
|
662
662
|
|
|
663
663
|
Uploads the data in *data* as part number *index* for the
|
|
664
664
|
associated file. If no value for *index* is given, *index*
|
|
@@ -163,7 +163,7 @@ def find_data_objects(classname=None, state=None, visibility=None,
|
|
|
163
163
|
:type level: string
|
|
164
164
|
:param region: Filter on result set by the given region(s).
|
|
165
165
|
:type region: string or list of strings
|
|
166
|
-
:param archival_state: Filter by the given archival state (one of "archived", "live", "archival",
|
|
166
|
+
:param archival_state: Filter by the given archival state (one of "archived", "live", "archival", "unarchiving", or "any"). Requires classname="file", project, and folder arguments to be provided.
|
|
167
167
|
:type archival_state: string
|
|
168
168
|
:param limit: The maximum number of results to be returned (if not specified, the number of results is unlimited)
|
|
169
169
|
:type limit: int
|
|
@@ -24,9 +24,8 @@ command-line client.
|
|
|
24
24
|
from __future__ import print_function, unicode_literals, division, absolute_import
|
|
25
25
|
|
|
26
26
|
import dxpy
|
|
27
|
-
import requests
|
|
28
27
|
from ..utils.resolver import (resolve_existing_path, resolve_path, is_hashid, get_last_pos_of_char)
|
|
29
|
-
from ..exceptions import (err_exit, DXCLIError)
|
|
28
|
+
from ..exceptions import (err_exit, DXCLIError, ResourceNotFound)
|
|
30
29
|
from . import try_call
|
|
31
30
|
from dxpy.utils.printing import (fill)
|
|
32
31
|
|
|
@@ -47,8 +46,8 @@ def cp_to_noexistent_destination(args, dest_path, dx_dest, dest_proj):
|
|
|
47
46
|
dest_name = dest_path[last_slash_pos + 1:].replace('\/', '/')
|
|
48
47
|
try:
|
|
49
48
|
dx_dest.list_folder(folder=dest_folder, only='folders')
|
|
50
|
-
except dxpy.DXAPIError as
|
|
51
|
-
if
|
|
49
|
+
except dxpy.DXAPIError as e:
|
|
50
|
+
if isinstance(e, ResourceNotFound):
|
|
52
51
|
raise DXCLIError('The destination folder does not exist')
|
|
53
52
|
else:
|
|
54
53
|
raise
|
|
@@ -25,8 +25,7 @@ import json
|
|
|
25
25
|
import traceback
|
|
26
26
|
import errno
|
|
27
27
|
import socket
|
|
28
|
-
import
|
|
29
|
-
from requests.exceptions import HTTPError
|
|
28
|
+
from urllib3.exceptions import HTTPError
|
|
30
29
|
|
|
31
30
|
import dxpy
|
|
32
31
|
from .compat import USING_PYTHON2
|
|
@@ -238,12 +237,12 @@ def exit_with_exc_info(code=1, message='', print_tb=False, exception=None):
|
|
|
238
237
|
sys.stderr.write('\n')
|
|
239
238
|
sys.exit(code)
|
|
240
239
|
|
|
241
|
-
network_exceptions = (
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
240
|
+
network_exceptions = (urllib3.exceptions.ProtocolError,
|
|
241
|
+
urllib3.exceptions.NewConnectionError,
|
|
242
|
+
urllib3.exceptions.DecodeError,
|
|
243
|
+
urllib3.exceptions.ConnectTimeoutError,
|
|
244
|
+
urllib3.exceptions.ReadTimeoutError,
|
|
245
|
+
urllib3.connectionpool.HTTPException,
|
|
247
246
|
urllib3.exceptions.SSLError,
|
|
248
247
|
ssl.SSLError,
|
|
249
248
|
HTTPError,
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
|
|
6
|
+
from dxpy import DXFile, config, upload_local_file
|
|
7
|
+
from dxpy.exceptions import err_exit
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ImageRef(object):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
process,
|
|
14
|
+
digest,
|
|
15
|
+
dx_file_id=None,
|
|
16
|
+
repository=None,
|
|
17
|
+
image_name=None,
|
|
18
|
+
tag=None
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
A class to handle an image reference from nextflow pipeline.
|
|
22
|
+
:param process: An NPA proces name (aka task name) which uses a given image
|
|
23
|
+
:type process: String
|
|
24
|
+
:param digest: An image digest
|
|
25
|
+
:type digest: String
|
|
26
|
+
:param dx_file_id: dx file id on the platform
|
|
27
|
+
:type dx_file_id: Optional[String]
|
|
28
|
+
:param repository: Image repository
|
|
29
|
+
:type repository: Optional[String]
|
|
30
|
+
:param image_name: Image name (usually a basename of the image referenced with repository)
|
|
31
|
+
:type image_name: Optional[String]
|
|
32
|
+
:param tag: A version tag
|
|
33
|
+
:type tag: Optional[String]
|
|
34
|
+
"""
|
|
35
|
+
self._caching_dir = os.path.join("/.cached_docker_images/", image_name or "")
|
|
36
|
+
self._dx_file_id = dx_file_id
|
|
37
|
+
self._bundled_depends = None
|
|
38
|
+
self._repository = repository
|
|
39
|
+
self._image_name = image_name
|
|
40
|
+
self._tag = tag
|
|
41
|
+
self._digest = digest
|
|
42
|
+
self._process = process
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def bundled_depends(self):
|
|
46
|
+
if not self._bundled_depends:
|
|
47
|
+
self._bundled_depends = self._package_bundle()
|
|
48
|
+
return self._bundled_depends
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def identifier(self):
|
|
52
|
+
return self._join_if_exists("_", [self._repository, self._image_name, self._tag, self._digest])
|
|
53
|
+
|
|
54
|
+
def _cache(self, file_name):
|
|
55
|
+
"""
|
|
56
|
+
Function to store an image on the platform as a dx file object. Should be implemented in subclasses.
|
|
57
|
+
:param file_name: A file name under which the image will be saved on the platform
|
|
58
|
+
:type file_name: String
|
|
59
|
+
:returns: Tuple[String, String] dx file id, file name (basename)
|
|
60
|
+
"""
|
|
61
|
+
raise NotImplementedError("Abstract class. Method not implemented. Use the concrete implementations.")
|
|
62
|
+
|
|
63
|
+
def _reconstruct_image_ref(self):
|
|
64
|
+
raise NotImplementedError("Abstract class. Method not implemented. Use the concrete implementations.")
|
|
65
|
+
|
|
66
|
+
def _construct_cache_file_name(self):
|
|
67
|
+
return self._join_if_exists("_", [self._image_name, self._tag])
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def _join_if_exists(delimiter, parts):
|
|
71
|
+
return delimiter.join([x for x in parts if x])
|
|
72
|
+
|
|
73
|
+
def _dx_file_get_name(self):
|
|
74
|
+
dx_file_handle = DXFile(self._dx_file_id, config["DX_PROJECT_CONTEXT_ID"])
|
|
75
|
+
return dx_file_handle.describe().get("name")
|
|
76
|
+
|
|
77
|
+
def _package_bundle(self):
|
|
78
|
+
"""
|
|
79
|
+
Function to include a container image stored on the platform into NPA
|
|
80
|
+
:returns: Dict in the format of {"name": "bundle.tar.gz", "id": {"$dnanexus_link": "file-xxxx"}}
|
|
81
|
+
"""
|
|
82
|
+
if not self._dx_file_id:
|
|
83
|
+
cache_file_name = self._construct_cache_file_name()
|
|
84
|
+
self._dx_file_id = self._cache(cache_file_name)
|
|
85
|
+
else:
|
|
86
|
+
cache_file_name = self._dx_file_get_name()
|
|
87
|
+
return {
|
|
88
|
+
"name": cache_file_name,
|
|
89
|
+
"id": {"$dnanexus_link": self._dx_file_id}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class DockerImageRef(ImageRef):
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
process,
|
|
97
|
+
digest,
|
|
98
|
+
dx_file_id=None,
|
|
99
|
+
repository=None,
|
|
100
|
+
image_name=None,
|
|
101
|
+
tag=None
|
|
102
|
+
):
|
|
103
|
+
super().__init__(
|
|
104
|
+
process,
|
|
105
|
+
digest,
|
|
106
|
+
dx_file_id,
|
|
107
|
+
repository,
|
|
108
|
+
image_name,
|
|
109
|
+
tag)
|
|
110
|
+
|
|
111
|
+
def _cache(self, file_name):
|
|
112
|
+
full_image_ref = self._reconstruct_image_ref()
|
|
113
|
+
docker_pull_cmd = "docker pull {}".format(full_image_ref)
|
|
114
|
+
docker_save_cmd = "docker save {} | gzip > {}".format(full_image_ref, file_name)
|
|
115
|
+
for cmd in [docker_pull_cmd, docker_save_cmd]:
|
|
116
|
+
try:
|
|
117
|
+
_ = subprocess.check_output(cmd, shell=True)
|
|
118
|
+
except subprocess.CalledProcessError:
|
|
119
|
+
err_exit("Failed to run a subprocess command: {}".format(cmd))
|
|
120
|
+
# may need wait_on_close = True??
|
|
121
|
+
uploaded_dx_file = upload_local_file(
|
|
122
|
+
filename=file_name,
|
|
123
|
+
project=config["DX_PROJECT_CONTEXT_ID"],
|
|
124
|
+
folder=self._caching_dir,
|
|
125
|
+
name=file_name,
|
|
126
|
+
parents=True,
|
|
127
|
+
properties={"image_digest": self._digest}
|
|
128
|
+
)
|
|
129
|
+
return uploaded_dx_file.get_id()
|
|
130
|
+
|
|
131
|
+
def _reconstruct_image_ref(self):
|
|
132
|
+
"""
|
|
133
|
+
Docker image reference has the form of <REPOSITORY_NAME>/<IMAGE_NAME>:<VERSION_TAG> or
|
|
134
|
+
<REPOSITORY_NAME>/<IMAGE_NAME>@<DIGEST>
|
|
135
|
+
"""
|
|
136
|
+
repo_and_image_name = self._join_if_exists("/", [self._repository, self._image_name])
|
|
137
|
+
if self._digest:
|
|
138
|
+
full_ref = self._join_if_exists("@", [repo_and_image_name, self._digest])
|
|
139
|
+
else:
|
|
140
|
+
full_ref = self._join_if_exists(":", [repo_and_image_name, self._tag])
|
|
141
|
+
return full_ref
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from dxpy.nextflow.ImageRef import DockerImageRef
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ImageRefFactoryError(Exception):
|
|
7
|
+
"""
|
|
8
|
+
Class to handle errors with instantiation of ImageRef subclasses
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ImageRefFactory(object):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
image_ref
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
A class to instantiate subclasses of ImageRef based on the container engine. Ususally instantiated after using the
|
|
19
|
+
nextaur:collect function to collect images for docker and other container engines.
|
|
20
|
+
:param image_ref: Image ref details
|
|
21
|
+
:type image_ref: Dict
|
|
22
|
+
"""
|
|
23
|
+
self._image_ref = image_ref
|
|
24
|
+
self._engine = image_ref.get("engine", None)
|
|
25
|
+
if not self._engine:
|
|
26
|
+
raise ImageRefFactoryError("Provide the container engine")
|
|
27
|
+
self._imageRef_switch = {
|
|
28
|
+
"docker": DockerImageRef
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def get_image(self):
|
|
32
|
+
image = self._imageRef_switch.get(self._engine, None)
|
|
33
|
+
if not image:
|
|
34
|
+
raise ImageRefFactoryError("Unsupported container engine: {}".format(self._engine))
|
|
35
|
+
return image(
|
|
36
|
+
process=self._image_ref["process"],
|
|
37
|
+
digest=self._image_ref["digest"],
|
|
38
|
+
dx_file_id=self._image_ref.get("dx_file_id", None),
|
|
39
|
+
repository=self._image_ref.get("repository", None),
|
|
40
|
+
image_name=self._image_ref.get("image_name", None),
|
|
41
|
+
tag=self._image_ref.get("tag", None)
|
|
42
|
+
)
|
|
43
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2013-2016 DNAnexus, Inc.
|
|
5
|
+
#
|
|
6
|
+
# This file is part of dx-toolkit (DNAnexus platform client libraries).
|
|
7
|
+
#
|
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
9
|
+
# use this file except in compliance with the License. You may obtain a copy
|
|
10
|
+
# of the License at
|
|
11
|
+
#
|
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
#
|
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
16
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
17
|
+
# License for the specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import subprocess
|
|
22
|
+
from dxpy.nextflow.ImageRefFactory import ImageRefFactory, ImageRefFactoryError
|
|
23
|
+
|
|
24
|
+
CONTAINERS_JSON = "containers.json"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def bundle_docker_images(image_refs):
|
|
28
|
+
"""
|
|
29
|
+
:param image_refs: Image references extracted from run_nextaur_collect().
|
|
30
|
+
:type image_refs: Dict
|
|
31
|
+
:returns: Array of dicts for bundledDepends attribute of the applet resources. Also saves images on the platform
|
|
32
|
+
if not done that before.
|
|
33
|
+
"""
|
|
34
|
+
image_factories = [ImageRefFactory(x) for x in image_refs]
|
|
35
|
+
images = [x.get_image() for x in image_factories]
|
|
36
|
+
seen_images = set()
|
|
37
|
+
bundled_depends = []
|
|
38
|
+
for image in images:
|
|
39
|
+
if image.identifier in seen_images:
|
|
40
|
+
continue
|
|
41
|
+
else:
|
|
42
|
+
bundled_depends.append(image.bundled_depends.copy())
|
|
43
|
+
seen_images.add(image.identifier)
|
|
44
|
+
return bundled_depends
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def run_nextaur_collect(resources_dir):
|
|
48
|
+
"""
|
|
49
|
+
:param resources_dir: URL to the local(ized) NF pipeline in the app(let) resources.
|
|
50
|
+
:type resources_dir: String
|
|
51
|
+
:returns: Dict. Image references in the form of
|
|
52
|
+
"process": String. Name of the process/task
|
|
53
|
+
"repository": String. Repository (host) prefix
|
|
54
|
+
"image_name": String. Image base name
|
|
55
|
+
"tag": String. Version tag
|
|
56
|
+
"digest": String. Image digest
|
|
57
|
+
"file_id": String. File ID if found on the platform
|
|
58
|
+
"engine": String. Container engine.
|
|
59
|
+
Runs nextaur:collect
|
|
60
|
+
"""
|
|
61
|
+
collect_cmd = "nextflow plugin nextaur:collect docker {}".format(resources_dir)
|
|
62
|
+
_ = subprocess.check_output(collect_cmd, shell=True)
|
|
63
|
+
with open(CONTAINERS_JSON, "r") as json_file:
|
|
64
|
+
image_refs = json.load(json_file).get("processes", None)
|
|
65
|
+
if not image_refs:
|
|
66
|
+
raise ImageRefFactoryError("Could not extract processes from nextaur:collect")
|
|
67
|
+
return image_refs
|
|
68
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"aws:ap-southeast-2": "record-Gb53JYj5Gxk7z6xggK6qxq1K",
|
|
3
|
+
"aws:eu-central-1": "record-Gb53PJj4Q67q34JzbxK3G51g",
|
|
4
|
+
"aws:eu-west-2-g": "record-Gb53Qx2KjP0JVbQ0xVZxZYq2",
|
|
5
|
+
"aws:us-east-1": "record-Gb53Fk00VYYyFBpQjxy65jYY",
|
|
6
|
+
"azure:westeurope": "record-Gb53YF0B54bb886BZj9jv16v",
|
|
7
|
+
"azure:westus": "record-Gb53bkj9Yk85kQjfJZJKJPQ6"
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"aws:ap-southeast-2": "record-GZvYK005pk0QG4FxZXqqgjKP",
|
|
3
|
+
"aws:eu-central-1": "record-GZvYPX04y9B66jXxk28qG7F0",
|
|
4
|
+
"aws:eu-west-2-g": "record-GZvYQgpKB7Y8pQJY8KbZF7kq",
|
|
5
|
+
"aws:us-east-1": "record-GZvY8bQ0057qKVy7G5G071jB",
|
|
6
|
+
"azure:westeurope": "record-GZvYYPjBF86q711V5VjGzFQ0",
|
|
7
|
+
"azure:westus": "record-GZvYfB09gx8gZpf049jPJQYg"
|
|
8
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
2
|
import os
|
|
3
3
|
import dxpy
|
|
4
4
|
import json
|
|
@@ -7,8 +7,7 @@ from glob import glob
|
|
|
7
7
|
import tempfile
|
|
8
8
|
|
|
9
9
|
from dxpy.nextflow.nextflow_templates import (get_nextflow_dxapp, get_nextflow_src)
|
|
10
|
-
from dxpy.nextflow.nextflow_utils import (get_template_dir, write_exec, write_dxapp, get_importer_name,
|
|
11
|
-
get_resources_dir_name, create_readme)
|
|
10
|
+
from dxpy.nextflow.nextflow_utils import (get_template_dir, write_exec, write_dxapp, get_importer_name, create_readme)
|
|
12
11
|
from dxpy.cli.exec_io import parse_obj
|
|
13
12
|
from dxpy.cli import try_call
|
|
14
13
|
from dxpy.utils.resolver import resolve_existing_path
|
|
@@ -99,7 +98,7 @@ def prepare_nextflow(resources_dir, profile, region):
|
|
|
99
98
|
dxapp_dir = tempfile.mkdtemp(prefix=".dx.nextflow")
|
|
100
99
|
|
|
101
100
|
custom_inputs = prepare_custom_inputs(schema_file=os.path.join(resources_dir, "nextflow_schema.json"))
|
|
102
|
-
dxapp_content = get_nextflow_dxapp(custom_inputs=custom_inputs,
|
|
101
|
+
dxapp_content = get_nextflow_dxapp(custom_inputs=custom_inputs, resources_dir=resources_dir,
|
|
103
102
|
region=region)
|
|
104
103
|
exec_content = get_nextflow_src(custom_inputs=custom_inputs, profile=profile, resources_dir=resources_dir)
|
|
105
104
|
copy_tree(get_template_dir(), dxapp_dir)
|