dxpy 0.392.0__tar.gz → 0.394.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.
Files changed (187) hide show
  1. {dxpy-0.392.0 → dxpy-0.394.0}/PKG-INFO +1 -1
  2. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/__init__.py +9 -9
  3. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxfile.py +20 -42
  4. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/nextflow_builder.py +2 -1
  5. dxpy-0.394.0/dxpy/toolkit_version.py +1 -0
  6. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy.egg-info/PKG-INFO +1 -1
  7. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dxclient.py +19 -0
  8. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dxpy.py +43 -21
  9. dxpy-0.392.0/dxpy/toolkit_version.py +0 -1
  10. {dxpy-0.392.0 → dxpy-0.394.0}/MANIFEST.in +0 -0
  11. {dxpy-0.392.0 → dxpy-0.394.0}/Readme.md +0 -0
  12. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/api.py +0 -0
  13. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/app_builder.py +0 -0
  14. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/app_categories.py +0 -0
  15. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/asset_builder.py +0 -0
  16. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/__init__.py +0 -0
  17. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/__init__.py +0 -0
  18. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/cmd_line_options_validator.py +0 -0
  19. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/data_transformations.py +0 -0
  20. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/dataset.py +0 -0
  21. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/json_validation_by_schema.py +0 -0
  22. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/schemas/__init__.py +0 -0
  23. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/schemas/assay_filtering_conditions.py +0 -0
  24. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/schemas/assay_filtering_json_schemas.py +0 -0
  25. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/schemas/input_arguments_validation_schemas.py +0 -0
  26. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/vizclient.py +0 -0
  27. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/vizserver_filters_from_json_parser.py +0 -0
  28. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/apollo/vizserver_payload_builder.py +0 -0
  29. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/auth.py +0 -0
  30. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/download_all_inputs.py +0 -0
  31. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxanalysis.py +0 -0
  32. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxapp.py +0 -0
  33. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxapp_container_functions.py +0 -0
  34. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxapplet.py +0 -0
  35. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxdatabase.py +0 -0
  36. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxdatabase_functions.py +0 -0
  37. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxdataobject_functions.py +0 -0
  38. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxfile_functions.py +0 -0
  39. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxglobalworkflow.py +0 -0
  40. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxjob.py +0 -0
  41. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxproject.py +0 -0
  42. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxrecord.py +0 -0
  43. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/dxworkflow.py +0 -0
  44. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/mount_all_inputs.py +0 -0
  45. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/bindings/search.py +0 -0
  46. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/__init__.py +0 -0
  47. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/cp.py +0 -0
  48. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/dataset_utilities.py +0 -0
  49. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/download.py +0 -0
  50. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/exec_io.py +0 -0
  51. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/help_messages.py +0 -0
  52. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/org.py +0 -0
  53. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/output_handling.py +0 -0
  54. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/parsers.py +0 -0
  55. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/cli/workflow.py +0 -0
  56. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/compat.py +0 -0
  57. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest.json +0 -0
  58. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest_staging.json +0 -0
  59. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest_staging_vep.json +0 -0
  60. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/Homo_sapiens_genes_manifest_vep.json +0 -0
  61. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/__init__.py +0 -0
  62. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/cohort_filter_payload.py +0 -0
  63. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/column_conditions.json +0 -0
  64. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/column_conversion.json +0 -0
  65. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/filter_to_payload.py +0 -0
  66. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/germline_utils.py +0 -0
  67. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/input_validation.py +0 -0
  68. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/input_validation_somatic.py +0 -0
  69. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/retrieve_allele_schema.json +0 -0
  70. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/retrieve_annotation_schema.json +0 -0
  71. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/retrieve_bins.py +0 -0
  72. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/retrieve_genotype_schema.json +0 -0
  73. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/return_columns_allele.json +0 -0
  74. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/return_columns_annotation.json +0 -0
  75. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/return_columns_genotype.json +0 -0
  76. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/return_columns_genotype_only.json +0 -0
  77. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dx_extract_utils/somatic_filter_payload.py +0 -0
  78. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/dxlog.py +0 -0
  79. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/exceptions.py +0 -0
  80. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/executable_builder.py +0 -0
  81. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/ImageRef.py +0 -0
  82. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/ImageRefFactory.py +0 -0
  83. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/__init__.py +0 -0
  84. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/awscli_assets.json +0 -0
  85. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/awscli_assets.staging.json +0 -0
  86. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/collect_images.py +0 -0
  87. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/nextaur_assets.json +0 -0
  88. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/nextaur_assets.staging.json +0 -0
  89. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/nextflow_assets.json +0 -0
  90. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/nextflow_assets.staging.json +0 -0
  91. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/nextflow_templates.py +0 -0
  92. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/nextflow/nextflow_utils.py +0 -0
  93. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/packages/__init__.py +0 -0
  94. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/scripts/__init__.py +0 -0
  95. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/scripts/dx.py +0 -0
  96. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/scripts/dx_app_wizard.py +0 -0
  97. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/scripts/dx_build_app.py +0 -0
  98. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/scripts/dx_build_applet.py +0 -0
  99. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/ssh_tunnel_app_support.py +0 -0
  100. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/system_requirements.py +0 -0
  101. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/__init__.py +0 -0
  102. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/bash.py +0 -0
  103. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/python.py +0 -0
  104. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/Readme.md +0 -0
  105. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/bash/basic/dxapp.json +0 -0
  106. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/bash/basic/src/code.sh +0 -0
  107. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/bash/parallelized/dxapp.json +0 -0
  108. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/bash/parallelized/src/code.sh +0 -0
  109. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/bash/scatter-process-gather/dxapp.json +0 -0
  110. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/bash/scatter-process-gather/src/code.sh +0 -0
  111. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/nextflow/dxapp.json +0 -0
  112. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/nextflow/src/nextflow.sh +0 -0
  113. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/basic/dxapp.json +0 -0
  114. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/basic/src/code.py +0 -0
  115. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/basic/test/test.py +0 -0
  116. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/parallelized/dxapp.json +0 -0
  117. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/parallelized/src/code.py +0 -0
  118. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/parallelized/test/test.py +0 -0
  119. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/scatter-process-gather/dxapp.json +0 -0
  120. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/scatter-process-gather/src/code.py +0 -0
  121. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/templates/python/scatter-process-gather/test/test.py +0 -0
  122. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/templating/utils.py +0 -0
  123. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/__init__.py +0 -0
  124. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/batch_utils.py +0 -0
  125. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/completer.py +0 -0
  126. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/config.py +0 -0
  127. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/describe.py +0 -0
  128. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/exec_utils.py +0 -0
  129. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/executable_unbuilder.py +0 -0
  130. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/file_handle.py +0 -0
  131. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/file_load_utils.py +0 -0
  132. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/genomic_utils.py +0 -0
  133. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/job_log_client.py +0 -0
  134. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/local_exec_utils.py +0 -0
  135. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/pathmatch.py +0 -0
  136. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/pretty_print.py +0 -0
  137. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/printing.py +0 -0
  138. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/resolver.py +0 -0
  139. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/spelling_corrector.py +0 -0
  140. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/utils/version.py +0 -0
  141. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy/workflow_builder.py +0 -0
  142. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy.egg-info/SOURCES.txt +0 -0
  143. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy.egg-info/dependency_links.txt +0 -0
  144. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy.egg-info/entry_points.txt +0 -0
  145. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy.egg-info/not-zip-safe +0 -0
  146. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy.egg-info/requires.txt +0 -0
  147. {dxpy-0.392.0 → dxpy-0.394.0}/dxpy.egg-info/top_level.txt +0 -0
  148. {dxpy-0.392.0 → dxpy-0.394.0}/requirements.txt +0 -0
  149. {dxpy-0.392.0 → dxpy-0.394.0}/requirements_setuptools.txt +0 -0
  150. {dxpy-0.392.0 → dxpy-0.394.0}/requirements_test.txt +0 -0
  151. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-clone-asset +0 -0
  152. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-docker +0 -0
  153. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-download-all-inputs +0 -0
  154. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-fetch-bundled-depends +0 -0
  155. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-generate-dxapp +0 -0
  156. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-jobutil-add-output +0 -0
  157. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-jobutil-dxlink +0 -0
  158. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-jobutil-get-identity-token +0 -0
  159. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-jobutil-new-job +0 -0
  160. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-jobutil-parse-link +0 -0
  161. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-jobutil-report-error +0 -0
  162. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-log-stream +0 -0
  163. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-mount-all-inputs +0 -0
  164. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-notebook-reconnect +0 -0
  165. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-print-bash-vars +0 -0
  166. {dxpy-0.392.0 → dxpy-0.394.0}/scripts/dx-upload-all-outputs +0 -0
  167. {dxpy-0.392.0 → dxpy-0.394.0}/setup.cfg +0 -0
  168. {dxpy-0.392.0 → dxpy-0.394.0}/setup.py +0 -0
  169. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_batch.py +0 -0
  170. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_create_cohort.py +0 -0
  171. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_describe.py +0 -0
  172. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dx-docker.py +0 -0
  173. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dx_app_wizard.py +0 -0
  174. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dx_bash_helpers.py +0 -0
  175. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dx_completion.py +0 -0
  176. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dx_symlink.py +0 -0
  177. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dxabs.py +0 -0
  178. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dxasset.py +0 -0
  179. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dxpy_utils.py +0 -0
  180. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_dxunpack.py +0 -0
  181. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_extract_assay.py +0 -0
  182. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_extract_dataset.py +0 -0
  183. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_extract_expression.py +0 -0
  184. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_extract_somatic.py +0 -0
  185. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_nextflow.py +0 -0
  186. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_nextflow_ImageRef.py +0 -0
  187. {dxpy-0.392.0 → dxpy-0.394.0}/test/test_nextflow_ImageRefFactory.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dxpy
3
- Version: 0.392.0
3
+ Version: 0.394.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
@@ -351,7 +351,7 @@ def _calculate_retry_delay(response, num_attempts):
351
351
  resource, including the most recent failed one
352
352
  :type num_attempts: int
353
353
  '''
354
- if response is not None and response.status == 503 and 'retry-after' in response.headers:
354
+ if response is not None and response.status in (503, 429) and 'retry-after' in response.headers:
355
355
  try:
356
356
  return int(response.headers['retry-after'])
357
357
  except ValueError:
@@ -553,10 +553,10 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
553
553
 
554
554
  # Maintain two separate counters for the number of tries...
555
555
 
556
- try_index = 0 # excluding 503 errors. The number of tries as given here
556
+ try_index = 0 # excluding 503/429 errors. The number of tries as given here
557
557
  # cannot exceed (max_retries + 1).
558
- try_index_including_503 = 0 # including 503 errors. This number is used to
559
- # do exponential backoff.
558
+ try_index_including_throttled = 0 # including 503/429 errors. This number is used to
559
+ # do exponential backoff.
560
560
 
561
561
  retried_responses = []
562
562
  _url = None
@@ -730,7 +730,7 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
730
730
  or isinstance(e, _RETRYABLE_WITH_RESPONSE)):
731
731
  ok_to_retry = is_retryable
732
732
  else:
733
- ok_to_retry = 500 <= response.status < 600
733
+ ok_to_retry = response.status == 429 or 500 <= response.status < 600
734
734
 
735
735
  # The server has closed the connection prematurely
736
736
  if (response is not None
@@ -753,10 +753,10 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
753
753
  if rewind_input_buffer_offset is not None:
754
754
  data.seek(rewind_input_buffer_offset)
755
755
 
756
- delay = _calculate_retry_delay(response, try_index_including_503 + 1)
756
+ delay = _calculate_retry_delay(response, try_index_including_throttled + 1)
757
757
 
758
758
  range_str = (' (range=%s)' % (headers['Range'],)) if 'Range' in headers else ''
759
- if response is not None and response.status == 503:
759
+ if response is not None and response.status in (503, 429):
760
760
  waiting_msg = 'Waiting %d seconds before retry...' % (delay,)
761
761
  else:
762
762
  waiting_msg = 'Waiting %d seconds before retry %d of %d...' % (
@@ -768,8 +768,8 @@ def DXHTTPRequest(resource, data, method='POST', headers=None, auth=True,
768
768
 
769
769
  logger.warning(log_msg)
770
770
  time.sleep(delay)
771
- try_index_including_503 += 1
772
- if response is None or response.status != 503:
771
+ try_index_including_throttled += 1
772
+ if response is None or response.status not in (503, 429):
773
773
  try_index += 1
774
774
  continue
775
775
 
@@ -253,6 +253,8 @@ class DXFile(DXDataObject):
253
253
  self._file_length = None
254
254
  self._cur_part = 1
255
255
  self._num_uploaded_parts = 0
256
+ # Cache for object existence in project
257
+ self._exists_in_proj = None
256
258
 
257
259
  def _new(self, dx_hash, media_type=None, **kwargs):
258
260
  """
@@ -314,39 +316,19 @@ class DXFile(DXDataObject):
314
316
  raise
315
317
 
316
318
  def __iter__(self):
317
- _buffer = self.read(self._read_bufsize)
318
- done = False
319
- if USING_PYTHON2:
320
- while not done:
321
- if b"\n" in _buffer:
322
- lines = _buffer.splitlines()
323
- for i in range(len(lines) - 1):
324
- yield lines[i]
325
- _buffer = lines[len(lines) - 1]
326
- else:
327
- more = self.read(self._read_bufsize)
328
- if more == b"":
329
- done = True
330
- else:
331
- _buffer = _buffer + more
332
- else:
333
- if self._binary_mode:
334
- raise DXFileError("Cannot read lines when file opened in binary mode")
335
- # python3 is much stricter about distinguishing
336
- # 'bytes' from 'str'.
337
- while not done:
338
- if "\n" in _buffer:
339
- lines = _buffer.splitlines()
340
- for i in range(len(lines) - 1):
341
- yield lines[i]
342
- _buffer = lines[len(lines) - 1]
343
- else:
344
- more = self.read(self._read_bufsize)
345
- if more == "":
346
- done = True
347
- else:
348
- _buffer = _buffer + more
349
-
319
+ _buffer = ""
320
+ if self._binary_mode:
321
+ raise DXFileError("Cannot read lines when file opened in binary mode")
322
+ while True:
323
+ more = self.read(self._read_bufsize)
324
+ if not more:
325
+ break
326
+ _buffer += more
327
+ parts = _buffer.split("\n")
328
+ for i in range(len(parts) - 1):
329
+ yield parts[i]
330
+ # The final piece may be incomplete, so keep it in the buffer
331
+ _buffer = parts[-1]
350
332
  if _buffer:
351
333
  yield _buffer
352
334
 
@@ -954,18 +936,14 @@ class DXFile(DXDataObject):
954
936
  buf.seek(orig_buf_pos)
955
937
  return buf.read()
956
938
 
957
- # Debug fallback
958
- # import urllib2
959
- # req = urllib2.Request(url, headers=headers)
960
- # response = urllib2.urlopen(req)
961
- # return response.read()
962
-
963
939
  def read(self, length=None, use_compression=None, project=None, **kwargs):
964
- if project is None and object_exists_in_project(self.get_id(), self.get_proj_id()):
940
+ # Check if the file is present in dxfile project attribute if the project arg not specified
941
+ if project is None and self._exists_in_proj is None and self.get_proj_id() is not None:
942
+ self._exists_in_proj = object_exists_in_project(self.get_id(), self.get_proj_id())
943
+ # Use the DXFile attribute if the project arg is not provided
944
+ if project is None and self._exists_in_proj:
965
945
  project = self.get_proj_id()
966
946
  data = self._read2(length=length, use_compression=use_compression, project=project, **kwargs)
967
- if USING_PYTHON2:
968
- return data
969
947
  # In python3, the underlying system methods use the 'bytes' type, not 'string'
970
948
  if self._binary_mode is True:
971
949
  return data
@@ -182,7 +182,8 @@ def prepare_custom_inputs(schema_file="./nextflow_schema.json"):
182
182
 
183
183
  with open(schema_file, "r") as fh:
184
184
  schema = json.load(fh)
185
- for d_key, d_schema in schema.get("definitions", {}).items():
185
+ defs_key = "definitions" if "definitions" in schema else "$defs" if "$defs" in schema else {}
186
+ for d_key, d_schema in schema.get(defs_key).items():
186
187
  required_inputs = d_schema.get("required", [])
187
188
  for property_key, property in d_schema.get("properties", {}).items():
188
189
  dx_input = {}
@@ -0,0 +1 @@
1
+ version = '0.394.0'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dxpy
3
- Version: 0.392.0
3
+ Version: 0.394.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
@@ -1964,6 +1964,25 @@ dxpy.run()
1964
1964
  run("cd {wd}; rm test; touch test".format(wd=wd))
1965
1965
  run("cd {wd}; dx download -f test".format(wd=wd))
1966
1966
  assert_md5_checksum(os.path.join(wd, "test"), hashlib.md5(part1 + part2))
1967
+
1968
+ def test_dx_download_api_call_count(self):
1969
+ """
1970
+ Ensure that dx download does not make more than 4 API calls as observed in _DX_DEBUG output.
1971
+ """
1972
+ with chdir(tempfile.mkdtemp()):
1973
+ # Create a test file and upload it
1974
+ test_file = dxpy.upload_string("test content", name="test_file", project=self.project, wait_on_close=True)
1975
+ test_file_id = test_file.get_id()
1976
+
1977
+ # Run dx download with _DX_DEBUG enabled
1978
+ (stdout, stderr) = run(f"_DX_DEBUG=1 dx download {test_file_id}", also_return_stderr=True)
1979
+
1980
+ # Count the number of API calls in the debug output
1981
+ api_call_count = len([line for line in stderr.splitlines() if line.startswith(">") and dxpy.APISERVER in line])
1982
+
1983
+ # Assert that no more than 4 API calls were made
1984
+ self.assertLessEqual(api_call_count, 4, f"dx download made {api_call_count} API calls, exceeding the limit of 4.\n_DX_DEBUG:\n{stderr}")
1985
+
1967
1986
 
1968
1987
 
1969
1988
  class TestDXClientDownloadDataEgressBilling(DXTestCase):
@@ -19,7 +19,7 @@
19
19
 
20
20
  from __future__ import print_function, unicode_literals, division, absolute_import
21
21
 
22
- import os, unittest, tempfile, filecmp, time, json, sys
22
+ import os, unittest, tempfile, filecmp, time, json, sys, random
23
23
  import shutil
24
24
  import string
25
25
  import subprocess
@@ -27,6 +27,7 @@ import platform
27
27
  import pytest
28
28
  import re
29
29
  import certifi
30
+ from mock import patch
30
31
 
31
32
  from urllib3.exceptions import SSLError, NewConnectionError
32
33
 
@@ -859,6 +860,47 @@ class TestDXFile(testutil.DXTestCaseCompat):
859
860
  dxpy.WORKSPACE_ID = workspace_id
860
861
 
861
862
  del os.environ['DX_JOB_ID']
863
+
864
+ def test_dxfile_read_api_call_count(self):
865
+ """
866
+ Ensure that dxfile.read() does not make more than 4 API calls by counting DXHTTPRequest invocations
867
+ """
868
+ print(sys.version)
869
+ with testutil.temporary_project() as project:
870
+ # Create a random 1KiB upload string
871
+ upload_string = ''.join(random.choices(string.ascii_lowercase + string.digits, k=1024))
872
+ dxfile = dxpy.upload_string(upload_string, project=project.get_id(), wait_on_close=True)
873
+
874
+ # Patch DXHTTPRequest to count its invocations
875
+ with patch("dxpy.DXHTTPRequest", wraps=dxpy.DXHTTPRequest) as mock_request:
876
+ # DXFile instantiated with project-id
877
+ dxfile.read()
878
+ # Count the number of DXHTTPRequest calls
879
+ api_call_count = mock_request.call_count
880
+ print(f"dxfile.read() made {api_call_count} API calls")
881
+ self.assertLessEqual(api_call_count, 4, f"dxfile.read() made {api_call_count} API calls, exceeding the limit.")
882
+ mock_request.reset_mock()
883
+
884
+ # DXFile instantiated without project-id
885
+ dxfile = dxpy.DXFile(dxfile.get_id(), project=None)
886
+ dxfile.read()
887
+ api_call_count = mock_request.call_count
888
+ print(f"dxfile.read() made {api_call_count} API calls")
889
+ self.assertLessEqual(api_call_count, 4, f"dxfile.read() made {api_call_count} API calls, exceeding the limit.")
890
+ mock_request.reset_mock()
891
+
892
+ # Call DXFile.read() 1024 times
893
+ dxfile = dxpy.DXFile(dxfile.get_id(), project=project.get_id())
894
+ while True:
895
+ data = dxfile.read(1)
896
+ if not data:
897
+ break
898
+ api_call_count = mock_request.call_count
899
+ print(f"dxfile.read() made {api_call_count} API calls")
900
+ self.assertLessEqual(api_call_count, 4, f"dxfile.read() made {api_call_count} API calls, exceeding the limit.")
901
+ mock_request.reset_mock()
902
+
903
+
862
904
 
863
905
 
864
906
  class TestFolder(unittest.TestCase):
@@ -2551,26 +2593,6 @@ class TestHTTPResponses(testutil.DXTestCaseCompat):
2551
2593
  with self.assertRaisesRegex((SSLError, IOError, OpenSSL.SSL.Error), "file"):
2552
2594
  dxpy.DXHTTPRequest("/system/whoami", {}, cert_file="nonexistent")
2553
2595
 
2554
- def test_fake_errors(self):
2555
- dxpy.DXHTTPRequest('/system/fakeError', {'errorType': 'Valid JSON'}, always_retry=True)
2556
-
2557
- # Minimal latency with retries, in seconds. This makes sure we actually did a retry.
2558
- min_sec_with_retries = 1
2559
- max_num_retries = 2
2560
- start_time = time.time()
2561
- with self.assertRaises(ValueError):
2562
- dxpy.DXHTTPRequest('/system/fakeError', {'errorType': 'Invalid JSON'},
2563
- max_retries=max_num_retries, always_retry=True)
2564
- end_time = time.time()
2565
- self.assertGreater(end_time - start_time, min_sec_with_retries)
2566
-
2567
- start_time = time.time()
2568
- with self.assertRaises(ValueError):
2569
- dxpy.DXHTTPRequest('/system/fakeError', {'errorType': 'Error not decodeable'},
2570
- max_retries=max_num_retries, always_retry=True)
2571
- end_time = time.time()
2572
- self.assertGreater(end_time - start_time, min_sec_with_retries)
2573
-
2574
2596
  def test_system_headers_user_agent(self):
2575
2597
  headers = dxpy.api.system_headers()
2576
2598
  self.assertTrue('user-agent' in headers)
@@ -1 +0,0 @@
1
- version = '0.392.0'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes