megadetector 5.0.10__py3-none-any.whl → 5.0.12__py3-none-any.whl

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.

Potentially problematic release.


This version of megadetector might be problematic. Click here for more details.

Files changed (226) hide show
  1. {api → megadetector/api}/batch_processing/api_core/batch_service/score.py +2 -2
  2. {api → megadetector/api}/synchronous/api_core/animal_detection_api/api_backend.py +1 -1
  3. {api → megadetector/api}/synchronous/api_core/animal_detection_api/api_frontend.py +1 -1
  4. {classification → megadetector/classification}/analyze_failed_images.py +3 -3
  5. {classification → megadetector/classification}/cache_batchapi_outputs.py +1 -1
  6. {classification → megadetector/classification}/create_classification_dataset.py +1 -1
  7. {classification → megadetector/classification}/crop_detections.py +1 -1
  8. {classification → megadetector/classification}/detect_and_crop.py +5 -5
  9. {classification → megadetector/classification}/evaluate_model.py +1 -1
  10. {classification → megadetector/classification}/json_to_azcopy_list.py +2 -2
  11. {classification → megadetector/classification}/json_validator.py +13 -9
  12. {classification → megadetector/classification}/map_classification_categories.py +1 -1
  13. {classification → megadetector/classification}/merge_classification_detection_output.py +1 -1
  14. {classification → megadetector/classification}/run_classifier.py +2 -1
  15. {classification → megadetector/classification}/train_classifier.py +8 -6
  16. {classification → megadetector/classification}/train_classifier_tf.py +10 -9
  17. {classification → megadetector/classification}/train_utils.py +3 -2
  18. {data_management → megadetector/data_management}/camtrap_dp_to_coco.py +4 -3
  19. {data_management → megadetector/data_management}/cct_json_utils.py +2 -2
  20. {data_management → megadetector/data_management}/cct_to_md.py +1 -1
  21. {data_management → megadetector/data_management}/coco_to_labelme.py +1 -1
  22. {data_management → megadetector/data_management}/coco_to_yolo.py +1 -1
  23. {data_management → megadetector/data_management}/databases/integrity_check_json_db.py +2 -2
  24. {data_management → megadetector/data_management}/get_image_sizes.py +4 -3
  25. {data_management → megadetector/data_management}/importers/auckland_doc_test_to_json.py +6 -5
  26. {data_management → megadetector/data_management}/importers/auckland_doc_to_json.py +4 -3
  27. {data_management → megadetector/data_management}/importers/awc_to_json.py +6 -4
  28. {data_management → megadetector/data_management}/importers/bellevue_to_json.py +3 -3
  29. {data_management → megadetector/data_management}/importers/cacophony-thermal-importer.py +4 -4
  30. {data_management → megadetector/data_management}/importers/carrizo_shrubfree_2018.py +5 -4
  31. {data_management → megadetector/data_management}/importers/carrizo_trail_cam_2017.py +8 -6
  32. {data_management → megadetector/data_management}/importers/cct_field_adjustments.py +2 -1
  33. {data_management → megadetector/data_management}/importers/channel_islands_to_cct.py +2 -2
  34. {data_management → megadetector/data_management}/importers/ena24_to_json.py +6 -5
  35. {data_management → megadetector/data_management}/importers/filenames_to_json.py +2 -1
  36. {data_management → megadetector/data_management}/importers/helena_to_cct.py +7 -6
  37. {data_management → megadetector/data_management}/importers/idaho-camera-traps.py +6 -6
  38. {data_management → megadetector/data_management}/importers/idfg_iwildcam_lila_prep.py +4 -4
  39. {data_management → megadetector/data_management}/importers/jb_csv_to_json.py +1 -1
  40. {data_management → megadetector/data_management}/importers/missouri_to_json.py +4 -3
  41. {data_management → megadetector/data_management}/importers/noaa_seals_2019.py +2 -2
  42. {data_management → megadetector/data_management}/importers/pc_to_json.py +5 -5
  43. {data_management → megadetector/data_management}/importers/prepare-noaa-fish-data-for-lila.py +3 -3
  44. {data_management → megadetector/data_management}/importers/prepare_zsl_imerit.py +3 -3
  45. {data_management → megadetector/data_management}/importers/rspb_to_json.py +2 -2
  46. {data_management → megadetector/data_management}/importers/save_the_elephants_survey_A.py +4 -4
  47. {data_management → megadetector/data_management}/importers/save_the_elephants_survey_B.py +6 -9
  48. {data_management → megadetector/data_management}/importers/snapshot_safari_importer.py +4 -4
  49. {data_management → megadetector/data_management}/importers/snapshot_safari_importer_reprise.py +2 -2
  50. {data_management → megadetector/data_management}/importers/snapshot_serengeti_lila.py +4 -4
  51. {data_management → megadetector/data_management}/importers/timelapse_csv_set_to_json.py +3 -3
  52. {data_management → megadetector/data_management}/importers/ubc_to_json.py +3 -3
  53. {data_management → megadetector/data_management}/importers/umn_to_json.py +2 -2
  54. {data_management → megadetector/data_management}/importers/wellington_to_json.py +3 -3
  55. {data_management → megadetector/data_management}/importers/wi_to_json.py +3 -2
  56. {data_management → megadetector/data_management}/labelme_to_coco.py +6 -7
  57. {data_management → megadetector/data_management}/labelme_to_yolo.py +2 -2
  58. {data_management → megadetector/data_management}/lila/add_locations_to_island_camera_traps.py +4 -4
  59. {data_management → megadetector/data_management}/lila/create_lila_blank_set.py +10 -9
  60. {data_management → megadetector/data_management}/lila/create_lila_test_set.py +3 -2
  61. {data_management → megadetector/data_management}/lila/create_links_to_md_results_files.py +1 -1
  62. {data_management → megadetector/data_management}/lila/download_lila_subset.py +5 -4
  63. {data_management → megadetector/data_management}/lila/generate_lila_per_image_labels.py +6 -5
  64. {data_management → megadetector/data_management}/lila/get_lila_annotation_counts.py +2 -2
  65. {data_management → megadetector/data_management}/lila/get_lila_image_counts.py +2 -1
  66. {data_management → megadetector/data_management}/lila/lila_common.py +5 -5
  67. {data_management → megadetector/data_management}/lila/test_lila_metadata_urls.py +2 -2
  68. {data_management → megadetector/data_management}/ocr_tools.py +6 -6
  69. {data_management → megadetector/data_management}/read_exif.py +2 -2
  70. {data_management → megadetector/data_management}/remap_coco_categories.py +1 -1
  71. {data_management → megadetector/data_management}/remove_exif.py +1 -1
  72. {data_management → megadetector/data_management}/resize_coco_dataset.py +4 -4
  73. {data_management → megadetector/data_management}/wi_download_csv_to_coco.py +3 -3
  74. {data_management → megadetector/data_management}/yolo_output_to_md_output.py +5 -5
  75. {data_management → megadetector/data_management}/yolo_to_coco.py +9 -9
  76. {detection → megadetector/detection}/process_video.py +9 -10
  77. {detection → megadetector/detection}/pytorch_detector.py +12 -8
  78. {detection → megadetector/detection}/run_detector.py +6 -6
  79. {detection → megadetector/detection}/run_detector_batch.py +12 -12
  80. {detection → megadetector/detection}/run_inference_with_yolov5_val.py +12 -12
  81. {detection → megadetector/detection}/run_tiled_inference.py +8 -9
  82. {detection → megadetector/detection}/tf_detector.py +3 -2
  83. {detection → megadetector/detection}/video_utils.py +2 -2
  84. {api/batch_processing → megadetector}/postprocessing/add_max_conf.py +1 -1
  85. {api/batch_processing → megadetector}/postprocessing/categorize_detections_by_size.py +1 -1
  86. {api/batch_processing → megadetector}/postprocessing/combine_api_outputs.py +1 -1
  87. {api/batch_processing → megadetector}/postprocessing/compare_batch_results.py +5 -5
  88. {api/batch_processing → megadetector}/postprocessing/convert_output_format.py +4 -5
  89. {api/batch_processing → megadetector}/postprocessing/load_api_results.py +1 -1
  90. {api/batch_processing → megadetector}/postprocessing/md_to_coco.py +3 -3
  91. {api/batch_processing → megadetector}/postprocessing/md_to_labelme.py +3 -3
  92. {api/batch_processing → megadetector}/postprocessing/merge_detections.py +1 -1
  93. {api/batch_processing → megadetector}/postprocessing/postprocess_batch_results.py +19 -21
  94. {api/batch_processing → megadetector}/postprocessing/remap_detection_categories.py +1 -1
  95. {api/batch_processing → megadetector}/postprocessing/render_detection_confusion_matrix.py +5 -6
  96. {api/batch_processing → megadetector}/postprocessing/repeat_detection_elimination/find_repeat_detections.py +3 -3
  97. {api/batch_processing → megadetector}/postprocessing/repeat_detection_elimination/remove_repeat_detections.py +3 -2
  98. {api/batch_processing → megadetector}/postprocessing/repeat_detection_elimination/repeat_detections_core.py +11 -11
  99. {api/batch_processing → megadetector}/postprocessing/separate_detections_into_folders.py +3 -4
  100. {api/batch_processing → megadetector}/postprocessing/subset_json_detector_output.py +2 -2
  101. {api/batch_processing → megadetector}/postprocessing/top_folders_to_bottom.py +1 -1
  102. {taxonomy_mapping → megadetector/taxonomy_mapping}/map_lila_taxonomy_to_wi_taxonomy.py +2 -2
  103. {taxonomy_mapping → megadetector/taxonomy_mapping}/map_new_lila_datasets.py +2 -6
  104. {taxonomy_mapping → megadetector/taxonomy_mapping}/preview_lila_taxonomy.py +6 -7
  105. {taxonomy_mapping → megadetector/taxonomy_mapping}/retrieve_sample_image.py +1 -1
  106. {taxonomy_mapping → megadetector/taxonomy_mapping}/simple_image_download.py +2 -1
  107. {taxonomy_mapping → megadetector/taxonomy_mapping}/species_lookup.py +1 -1
  108. {taxonomy_mapping → megadetector/taxonomy_mapping}/taxonomy_csv_checker.py +1 -1
  109. {taxonomy_mapping → megadetector/taxonomy_mapping}/validate_lila_category_mappings.py +1 -1
  110. {md_utils → megadetector/utils}/azure_utils.py +7 -3
  111. {md_utils → megadetector/utils}/directory_listing.py +1 -1
  112. {md_utils → megadetector/utils}/md_tests.py +29 -29
  113. {md_utils → megadetector/utils}/split_locations_into_train_val.py +1 -1
  114. {md_utils → megadetector/utils}/write_html_image_list.py +1 -1
  115. {md_visualization → megadetector/visualization}/render_images_with_thumbnails.py +3 -3
  116. {md_visualization → megadetector/visualization}/visualization_utils.py +6 -7
  117. {md_visualization → megadetector/visualization}/visualize_db.py +3 -4
  118. {md_visualization → megadetector/visualization}/visualize_detector_output.py +9 -10
  119. {megadetector-5.0.10.dist-info → megadetector-5.0.12.dist-info}/LICENSE +0 -0
  120. {megadetector-5.0.10.dist-info → megadetector-5.0.12.dist-info}/METADATA +12 -11
  121. megadetector-5.0.12.dist-info/RECORD +199 -0
  122. megadetector-5.0.12.dist-info/top_level.txt +1 -0
  123. api/batch_processing/data_preparation/manage_local_batch.py +0 -2391
  124. api/batch_processing/data_preparation/manage_video_batch.py +0 -327
  125. api/synchronous/api_core/animal_detection_api/data_management/annotations/annotation_constants.py +0 -47
  126. api/synchronous/api_core/animal_detection_api/detection/detector_training/copy_checkpoints.py +0 -43
  127. api/synchronous/api_core/animal_detection_api/detection/process_video.py +0 -543
  128. api/synchronous/api_core/animal_detection_api/detection/pytorch_detector.py +0 -304
  129. api/synchronous/api_core/animal_detection_api/detection/run_detector.py +0 -627
  130. api/synchronous/api_core/animal_detection_api/detection/run_detector_batch.py +0 -1029
  131. api/synchronous/api_core/animal_detection_api/detection/run_inference_with_yolov5_val.py +0 -581
  132. api/synchronous/api_core/animal_detection_api/detection/run_tiled_inference.py +0 -754
  133. api/synchronous/api_core/animal_detection_api/detection/tf_detector.py +0 -165
  134. api/synchronous/api_core/animal_detection_api/detection/video_utils.py +0 -495
  135. api/synchronous/api_core/animal_detection_api/md_utils/azure_utils.py +0 -174
  136. api/synchronous/api_core/animal_detection_api/md_utils/ct_utils.py +0 -262
  137. api/synchronous/api_core/animal_detection_api/md_utils/directory_listing.py +0 -251
  138. api/synchronous/api_core/animal_detection_api/md_utils/matlab_porting_tools.py +0 -97
  139. api/synchronous/api_core/animal_detection_api/md_utils/path_utils.py +0 -416
  140. api/synchronous/api_core/animal_detection_api/md_utils/process_utils.py +0 -110
  141. api/synchronous/api_core/animal_detection_api/md_utils/sas_blob_utils.py +0 -509
  142. api/synchronous/api_core/animal_detection_api/md_utils/string_utils.py +0 -59
  143. api/synchronous/api_core/animal_detection_api/md_utils/url_utils.py +0 -144
  144. api/synchronous/api_core/animal_detection_api/md_utils/write_html_image_list.py +0 -226
  145. api/synchronous/api_core/animal_detection_api/md_visualization/visualization_utils.py +0 -841
  146. detection/detector_training/model_main_tf2.py +0 -114
  147. docs/source/conf.py +0 -43
  148. megadetector-5.0.10.dist-info/RECORD +0 -224
  149. megadetector-5.0.10.dist-info/top_level.txt +0 -8
  150. {api → megadetector/api}/__init__.py +0 -0
  151. {api → megadetector/api}/batch_processing/__init__.py +0 -0
  152. {api → megadetector/api}/batch_processing/api_core/__init__.py +0 -0
  153. {api → megadetector/api}/batch_processing/api_core/batch_service/__init__.py +0 -0
  154. {api → megadetector/api}/batch_processing/api_core/server.py +0 -0
  155. {api → megadetector/api}/batch_processing/api_core/server_api_config.py +0 -0
  156. {api → megadetector/api}/batch_processing/api_core/server_app_config.py +0 -0
  157. {api → megadetector/api}/batch_processing/api_core/server_batch_job_manager.py +0 -0
  158. {api → megadetector/api}/batch_processing/api_core/server_job_status_table.py +0 -0
  159. {api → megadetector/api}/batch_processing/api_core/server_orchestration.py +0 -0
  160. {api → megadetector/api}/batch_processing/api_core/server_utils.py +0 -0
  161. {api → megadetector/api}/batch_processing/api_core_support/__init__.py +0 -0
  162. {api → megadetector/api}/batch_processing/api_core_support/aggregate_results_manually.py +0 -0
  163. {api → megadetector/api}/batch_processing/api_support/__init__.py +0 -0
  164. {api → megadetector/api}/batch_processing/api_support/summarize_daily_activity.py +0 -0
  165. {api → megadetector/api}/batch_processing/data_preparation/__init__.py +0 -0
  166. {api → megadetector/api}/batch_processing/integration/digiKam/setup.py +0 -0
  167. {api → megadetector/api}/batch_processing/integration/digiKam/xmp_integration.py +0 -0
  168. {api → megadetector/api}/batch_processing/integration/eMammal/test_scripts/config_template.py +0 -0
  169. {api → megadetector/api}/batch_processing/integration/eMammal/test_scripts/push_annotations_to_emammal.py +0 -0
  170. {api → megadetector/api}/batch_processing/integration/eMammal/test_scripts/select_images_for_testing.py +0 -0
  171. {api/batch_processing/postprocessing → megadetector/api/synchronous}/__init__.py +0 -0
  172. {api/synchronous → megadetector/api/synchronous/api_core/animal_detection_api}/__init__.py +0 -0
  173. {api → megadetector/api}/synchronous/api_core/animal_detection_api/config.py +0 -0
  174. {api/synchronous/api_core/animal_detection_api → megadetector/api/synchronous/api_core/tests}/__init__.py +0 -0
  175. {api → megadetector/api}/synchronous/api_core/tests/load_test.py +0 -0
  176. {api/synchronous/api_core/tests → megadetector/classification}/__init__.py +0 -0
  177. {classification → megadetector/classification}/aggregate_classifier_probs.py +0 -0
  178. {classification → megadetector/classification}/csv_to_json.py +0 -0
  179. {classification → megadetector/classification}/efficientnet/__init__.py +0 -0
  180. {classification → megadetector/classification}/efficientnet/model.py +0 -0
  181. {classification → megadetector/classification}/efficientnet/utils.py +0 -0
  182. {classification → megadetector/classification}/identify_mislabeled_candidates.py +0 -0
  183. {classification → megadetector/classification}/prepare_classification_script.py +0 -0
  184. {classification → megadetector/classification}/prepare_classification_script_mc.py +0 -0
  185. {classification → megadetector/classification}/save_mislabeled.py +0 -0
  186. {classification → megadetector/data_management}/__init__.py +0 -0
  187. {data_management → megadetector/data_management/annotations}/__init__.py +0 -0
  188. {data_management → megadetector/data_management}/annotations/annotation_constants.py +0 -0
  189. {data_management → megadetector/data_management}/cct_to_wi.py +0 -0
  190. {data_management/annotations → megadetector/data_management/databases}/__init__.py +0 -0
  191. {data_management → megadetector/data_management}/databases/add_width_and_height_to_db.py +0 -0
  192. {data_management → megadetector/data_management}/databases/combine_coco_camera_traps_files.py +0 -0
  193. {data_management → megadetector/data_management}/databases/subset_json_db.py +0 -0
  194. {data_management → megadetector/data_management}/generate_crops_from_cct.py +0 -0
  195. {data_management → megadetector/data_management}/importers/add_nacti_sizes.py +0 -0
  196. {data_management → megadetector/data_management}/importers/add_timestamps_to_icct.py +0 -0
  197. {data_management → megadetector/data_management}/importers/animl_results_to_md_results.py +0 -0
  198. {data_management → megadetector/data_management}/importers/eMammal/copy_and_unzip_emammal.py +0 -0
  199. {data_management → megadetector/data_management}/importers/eMammal/eMammal_helpers.py +0 -0
  200. {data_management → megadetector/data_management}/importers/eMammal/make_eMammal_json.py +0 -0
  201. {data_management → megadetector/data_management}/importers/mcgill_to_json.py +0 -0
  202. {data_management → megadetector/data_management}/importers/nacti_fieldname_adjustments.py +0 -0
  203. {data_management → megadetector/data_management}/importers/plot_wni_giraffes.py +0 -0
  204. {data_management → megadetector/data_management}/importers/snapshotserengeti/make_full_SS_json.py +0 -0
  205. {data_management → megadetector/data_management}/importers/snapshotserengeti/make_per_season_SS_json.py +0 -0
  206. {data_management → megadetector/data_management}/importers/sulross_get_exif.py +0 -0
  207. {data_management → megadetector/data_management}/importers/zamba_results_to_md_results.py +0 -0
  208. {data_management/databases → megadetector/data_management/lila}/__init__.py +0 -0
  209. {data_management → megadetector/data_management}/lila/add_locations_to_nacti.py +0 -0
  210. {data_management/lila → megadetector/detection}/__init__.py +0 -0
  211. {detection → megadetector/detection/detector_training}/__init__.py +0 -0
  212. {api/synchronous/api_core/animal_detection_api → megadetector}/detection/detector_training/model_main_tf2.py +0 -0
  213. {detection/detector_training → megadetector/postprocessing}/__init__.py +0 -0
  214. {md_utils → megadetector/taxonomy_mapping}/__init__.py +0 -0
  215. {taxonomy_mapping → megadetector/taxonomy_mapping}/prepare_lila_taxonomy_release.py +0 -0
  216. {taxonomy_mapping → megadetector/taxonomy_mapping}/taxonomy_graph.py +0 -0
  217. {md_visualization → megadetector/utils}/__init__.py +0 -0
  218. {md_utils → megadetector/utils}/ct_utils.py +0 -0
  219. {md_utils → megadetector/utils}/path_utils.py +0 -0
  220. {md_utils → megadetector/utils}/process_utils.py +0 -0
  221. {md_utils → megadetector/utils}/sas_blob_utils.py +0 -0
  222. {md_utils → megadetector/utils}/string_utils.py +0 -0
  223. {md_utils → megadetector/utils}/url_utils.py +0 -0
  224. {taxonomy_mapping → megadetector/visualization}/__init__.py +0 -0
  225. {md_visualization → megadetector/visualization}/plot_utils.py +0 -0
  226. {megadetector-5.0.10.dist-info → megadetector-5.0.12.dist-info}/WHEEL +0 -0
@@ -1,509 +0,0 @@
1
- ########
2
- #
3
- # sas_blob_utils.py
4
- #
5
- # This module contains helper functions for dealing with Shared Access Signatures
6
- # (SAS) tokens for Azure Blob Storage.
7
- #
8
- # The default Azure Storage SAS URI format is:
9
- #
10
- # https://<account>.blob.core.windows.net/<container>/<blob>?<sas_token>
11
- #
12
- # This module assumes azure-storage-blob version 12.5.
13
- #
14
- # Documentation for Azure Blob Storage:
15
- # docs.microsoft.com/en-us/azure/developer/python/sdk/storage/storage-blob-readme
16
- #
17
- # Documentation for SAS:
18
- # docs.microsoft.com/en-us/azure/storage/common/storage-sas-overview
19
- #
20
- ########
21
-
22
- #%% Imports
23
-
24
- from datetime import datetime, timedelta
25
- import io
26
- import re
27
- from typing import (Any, AnyStr, Dict, IO, Iterable, List, Optional, Set, Tuple, Union)
28
- from urllib import parse
29
- import uuid
30
-
31
- from tqdm import tqdm
32
-
33
- from azure.storage.blob import (
34
- BlobClient,
35
- BlobProperties,
36
- ContainerClient,
37
- ContainerSasPermissions,
38
- generate_container_sas)
39
-
40
-
41
- #%% URI management
42
-
43
- def build_azure_storage_uri(
44
- account: str,
45
- container: Optional[str] = None,
46
- blob: Optional[str] = None,
47
- sas_token: Optional[str] = None,
48
- account_url_template: str = 'https://{account}.blob.core.windows.net'
49
- ) -> str:
50
- """
51
- Args:
52
- account: str, name of Azure Storage account
53
- container: optional str, name of Azure Blob Storage container
54
- blob: optional str, name of blob, not URL-escaped
55
- if blob is given, must also specify container
56
- sas_token: optional str, Shared Access Signature (SAS). Leading ?
57
- is removed if present.
58
- account_url_template: str, Python 3 string formatting template,
59
- contains '{account}' placeholder, defaults to default Azure
60
- Storage URL format. Set this value if using Azurite Azure Storage
61
- emulator.
62
-
63
- Returns: str, Azure storage URI
64
- """
65
-
66
- uri = account_url_template.format(account=account)
67
- if container is not None:
68
- uri = f'{uri}/{container}'
69
- if blob is not None:
70
- assert container is not None
71
- blob = parse.quote(blob)
72
- uri = f'{uri}/{blob}'
73
- if sas_token is not None:
74
- if sas_token[0] == '?':
75
- sas_token = sas_token[1:]
76
- uri = f'{uri}?{sas_token}'
77
- return uri
78
-
79
-
80
- def _get_resource_reference(prefix: str) -> str:
81
- return '{}{}'.format(prefix, str(uuid.uuid4()).replace('-', ''))
82
-
83
-
84
- def get_client_from_uri(container_uri: str) -> ContainerClient:
85
- """
86
- Gets a ContainerClient for the given container URI.
87
- """
88
-
89
- return ContainerClient.from_container_url(container_uri)
90
-
91
-
92
- def get_account_from_uri(sas_uri: str) -> str:
93
- """
94
- Assumes that sas_uri points to Azure Blob Storage account hosted at
95
- a default Azure URI. Does not work for locally-emulated Azure Storage
96
- or Azure Storage hosted at custom endpoints.
97
- """
98
-
99
- url_parts = parse.urlsplit(sas_uri)
100
- loc = url_parts.netloc # "<account>.blob.core.windows.net"
101
- return loc.split('.')[0]
102
-
103
-
104
- def is_container_uri(sas_uri: str) -> bool:
105
- """
106
- Returns True if the signed resource field in the URI "sr" is a container "c"
107
- or a directory "d"
108
- """
109
-
110
- data = get_all_query_parts(sas_uri)
111
- if 'sr' not in data:
112
- return False
113
-
114
- if 'c' in data['sr'] or 'd' in data['sr']:
115
- return True
116
- else:
117
- return False
118
-
119
- def is_blob_uri(sas_uri: str) -> bool:
120
- """
121
- Returns True if the signed resource field in the URI "sr" is a blob "b".
122
- """
123
-
124
- data = get_all_query_parts(sas_uri)
125
- if 'sr' not in data:
126
- return False
127
-
128
- if 'b' in data['sr']:
129
- return True
130
- else:
131
- return False
132
-
133
-
134
- def get_container_from_uri(sas_uri: str, unquote: bool = True) -> str:
135
- """
136
- Gets the container name from a Azure Blob Storage URI.
137
-
138
- Assumes that sas_uri points to Azure Blob Storage account hosted at
139
- a default Azure URI. Does not work for locally-emulated Azure Storage
140
- or Azure Storage hosted at custom endpoints.
141
-
142
- Args:
143
- sas_uri: str, Azure blob storage URI, may include SAS token
144
- unquote: bool, whether to replace any %xx escapes by their
145
- single-character equivalent, default True
146
-
147
- Returns: str, container name
148
-
149
- Raises: ValueError, if sas_uri does not include a container
150
- """
151
-
152
- url_parts = parse.urlsplit(sas_uri)
153
- raw_path = url_parts.path.lstrip('/') # remove leading "/" from path
154
- container = raw_path.split('/')[0]
155
- if container == '':
156
- raise ValueError('Given sas_uri does not include a container.')
157
- if unquote:
158
- container = parse.unquote(container)
159
- return container
160
-
161
-
162
- def get_blob_from_uri(sas_uri: str, unquote: bool = True) -> str:
163
- """
164
- Return the path to the blob from the root container if this sas_uri
165
- is for an individual blob; otherwise returns None.
166
-
167
- Args:
168
- sas_uri: str, Azure blob storage URI, may include SAS token
169
- unquote: bool, whether to replace any %xx escapes by their
170
- single-character equivalent, default True
171
-
172
- Returns: str, blob name (path to the blob from the root container)
173
-
174
- Raises: ValueError, if sas_uri does not include a blob name
175
- """
176
-
177
- # Get the entire path with all slashes after the container
178
- url_parts = parse.urlsplit(sas_uri)
179
- raw_path = url_parts.path.lstrip('/') # remove leading "/" from path
180
- parts = raw_path.split('/', maxsplit=1)
181
- if len(parts) < 2 or parts[1] == '':
182
- raise ValueError('Given sas_uri does not include a blob name')
183
-
184
- blob = parts[1] # first item is an empty string
185
- if unquote:
186
- blob = parse.unquote(blob)
187
- return blob
188
-
189
-
190
- def get_sas_token_from_uri(sas_uri: str) -> Optional[str]:
191
- """
192
- Get the query part of the SAS token that contains permissions, access
193
- times and signature.
194
-
195
- Args:
196
- sas_uri: str, Azure blob storage SAS token
197
-
198
- Returns: str, query part of the SAS token (without leading '?'),
199
- or None if URI has no token.
200
- """
201
-
202
- url_parts = parse.urlsplit(sas_uri)
203
- sas_token = url_parts.query or None # None if query is empty string
204
- return sas_token
205
-
206
-
207
- def get_resource_type_from_uri(sas_uri: str) -> Optional[str]:
208
- """
209
- Get the resource type pointed to by this SAS token.
210
-
211
- Args:
212
- sas_uri: str, Azure blob storage URI with SAS token
213
-
214
- Returns: A string (either 'blob' or 'container') or None.
215
- """
216
-
217
- url_parts = parse.urlsplit(sas_uri)
218
- data = parse.parse_qs(url_parts.query)
219
- if 'sr' in data:
220
- types = data['sr']
221
- if 'b' in types:
222
- return 'blob'
223
- elif 'c' in types:
224
- return 'container'
225
- return None
226
-
227
-
228
- def get_endpoint_suffix(sas_uri):
229
- """
230
- Gets the endpoint at which the blob storage account is served.
231
- Args:
232
- sas_uri: str, Azure blob storage URI with SAS token
233
-
234
- Returns: A string, usually 'core.windows.net' or 'core.chinacloudapi.cn', to
235
- use for the `endpoint` argument in various blob storage SDK functions.
236
- """
237
-
238
- url_parts = parse.urlsplit(sas_uri)
239
- suffix = url_parts.netloc.split('.blob.')[1].split('/')[0]
240
- return suffix
241
-
242
-
243
- def get_permissions_from_uri(sas_uri: str) -> Set[str]:
244
- """
245
- Get the permissions given by this SAS token.
246
-
247
- Args:
248
- sas_uri: str, Azure blob storage URI with SAS token
249
-
250
- Returns: A set containing some of 'read', 'write', 'delete' and 'list'.
251
- Empty set returned if no permission specified in sas_uri.
252
- """
253
-
254
- data = get_all_query_parts(sas_uri)
255
- permissions_set = set()
256
- if 'sp' in data:
257
- permissions = data['sp'][0]
258
- if 'r' in permissions:
259
- permissions_set.add('read')
260
- if 'w' in permissions:
261
- permissions_set.add('write')
262
- if 'd' in permissions:
263
- permissions_set.add('delete')
264
- if 'l' in permissions:
265
- permissions_set.add('list')
266
- return permissions_set
267
-
268
-
269
- def get_all_query_parts(sas_uri: str) -> Dict[str, Any]:
270
- """
271
- Gets the SAS token parameters.
272
- """
273
-
274
- url_parts = parse.urlsplit(sas_uri)
275
- return parse.parse_qs(url_parts.query)
276
-
277
-
278
- #%% Blob
279
-
280
- def check_blob_exists(sas_uri: str, blob_name: Optional[str] = None) -> bool:
281
- """
282
- Checks whether a given URI points to an actual blob.
283
-
284
- Assumes that sas_uri points to Azure Blob Storage account hosted at
285
- a default Azure URI. Does not work for locally-emulated Azure Storage
286
- or Azure Storage hosted at custom endpoints. In these cases, create a
287
- BlobClient using the default constructor, instead of from_blob_url(),
288
- and use the BlobClient.exists() method directly.
289
-
290
- Args:
291
- sas_uri: str, URI to a container or a blob
292
- if blob_name is given, sas_uri is treated as a container URI
293
- otherwise, sas_uri is treated as a blob URI
294
- blob_name: optional str, name of blob, not URL-escaped
295
- must be given if sas_uri is a URI to a container
296
-
297
- Returns: bool, whether the sas_uri given points to an existing blob
298
- """
299
-
300
- if blob_name is not None:
301
- sas_uri = build_blob_uri(
302
- container_uri=sas_uri, blob_name=blob_name)
303
-
304
- with BlobClient.from_blob_url(sas_uri) as blob_client:
305
- return blob_client.exists()
306
-
307
-
308
- def upload_blob(container_uri: str, blob_name: str,
309
- data: Union[Iterable[AnyStr], IO[AnyStr]],
310
- overwrite: bool = False) -> str:
311
- """
312
- Creates a new blob of the given name from an IO stream.
313
-
314
- Args:
315
- container_uri: str, URI to a container, may include SAS token
316
- blob_name: str, name of blob to upload
317
- data: str, bytes, or IO stream
318
- if str, assumes utf-8 encoding
319
- overwrite: bool, whether to overwrite existing blob (if any)
320
-
321
- Returns: str, URL to blob, includes SAS token if container_uri has SAS token
322
- """
323
-
324
- account_url, container, sas_token = split_container_uri(container_uri)
325
- with BlobClient(account_url=account_url, container_name=container,
326
- blob_name=blob_name, credential=sas_token) as blob_client:
327
- blob_client.upload_blob(data, overwrite=overwrite)
328
- return blob_client.url
329
-
330
-
331
- def download_blob_to_stream(sas_uri: str) -> Tuple[io.BytesIO, BlobProperties]:
332
- """
333
- Downloads a blob to an IO stream.
334
-
335
- Args:
336
- sas_uri: str, URI to a blob
337
-
338
- Returns:
339
- output_stream: io.BytesIO, remember to close it when finished using
340
- blob_properties: BlobProperties
341
-
342
- Raises: azure.core.exceptions.ResourceNotFoundError, if sas_uri points
343
- to a non-existent blob
344
- """
345
-
346
- with BlobClient.from_blob_url(sas_uri) as blob_client:
347
- output_stream = io.BytesIO()
348
- blob_client.download_blob().readinto(output_stream)
349
- output_stream.seek(0)
350
- blob_properties = blob_client.get_blob_properties()
351
- return output_stream, blob_properties
352
-
353
-
354
- def build_blob_uri(container_uri: str, blob_name: str) -> str:
355
- """
356
- Args:
357
- container_uri: str, URI to blob storage container
358
- <account_url>/<container>?<sas_token>
359
- blob_name: str, name of blob, not URL-escaped
360
-
361
- Returns: str, blob URI <account_url>/<container>/<blob_name>?<sas_token>,
362
- <blob_name> is URL-escaped
363
- """
364
-
365
- account_url, container, sas_token = split_container_uri(container_uri)
366
-
367
- blob_name = parse.quote(blob_name)
368
- blob_uri = f'{account_url}/{container}/{blob_name}'
369
- if sas_token is not None:
370
- blob_uri += f'?{sas_token}'
371
- return blob_uri
372
-
373
-
374
- #%% Container
375
-
376
- def list_blobs_in_container(
377
- container_uri: str,
378
- blob_prefix: Optional[str] = None,
379
- blob_suffix: Optional[Union[str, Tuple[str]]] = None,
380
- rsearch: Optional[str] = None,
381
- limit: Optional[int] = None,
382
- verbose: Optional[bool] = True
383
- ) -> List[str]:
384
- """
385
- Get a sorted list of blob names in this container.
386
-
387
- Args:
388
- container_uri: str, URI to a container, may include SAS token
389
- blob_prefix: optional str, returned results will only contain blob names
390
- to with this prefix
391
- blob_suffix: optional str or tuple of str, returned results will only
392
- contain blob names with this/these suffix(es). The blob names will
393
- be lowercased first before comparing with the suffix(es).
394
- rsearch: optional str, returned results will only contain blob names
395
- that match this regex. Can also be a list of regexes, in which case
396
- blobs matching *any* of the regex's will be returned.
397
- limit: int, maximum # of blob names to list
398
- if None, then returns all blob names
399
-
400
- Returns:
401
- sorted list of blob names, of length limit or shorter.
402
- """
403
-
404
- if verbose:
405
- print('Listing blobs')
406
-
407
- if (get_sas_token_from_uri(container_uri) is not None
408
- and get_resource_type_from_uri(container_uri) != 'container'):
409
- raise ValueError('The SAS token provided is not for a container.')
410
-
411
- if blob_prefix is not None and not isinstance(blob_prefix, str):
412
- raise ValueError('blob_prefix must be a str.')
413
-
414
- if (blob_suffix is not None
415
- and not isinstance(blob_suffix, str)
416
- and not isinstance(blob_suffix, tuple)):
417
- raise ValueError('blob_suffix must be a str or a tuple of strings')
418
-
419
- list_blobs = []
420
- with get_client_from_uri(container_uri) as container_client:
421
- generator = container_client.list_blobs(
422
- name_starts_with=blob_prefix)
423
-
424
- if blob_suffix is None and rsearch is None:
425
- list_blobs = [blob.name for blob in tqdm(generator,disable=(not verbose))]
426
- i = len(list_blobs)
427
- else:
428
- i = 0
429
- for blob in tqdm(generator,disable=(not verbose)):
430
- i += 1
431
- suffix_ok = (blob_suffix is None
432
- or blob.name.lower().endswith(blob_suffix))
433
- regex_ok = False
434
- if rsearch is None:
435
- regex_ok = True
436
- else:
437
- if not isinstance(rsearch, list):
438
- rsearch = [rsearch]
439
- # Check whether this blob name matches *any* of our regex's
440
- for expr in rsearch:
441
- if re.search(expr, blob.name) is not None:
442
- regex_ok = True
443
- break
444
- if suffix_ok and regex_ok:
445
- list_blobs.append(blob.name)
446
- if limit is not None and len(list_blobs) == limit:
447
- break
448
-
449
- if verbose:
450
- print(f'Enumerated {len(list_blobs)} matching blobs out of {i} total')
451
-
452
- return sorted(list_blobs) # sort for determinism
453
-
454
-
455
- def generate_writable_container_sas(account_name: str,
456
- account_key: str,
457
- container_name: str,
458
- access_duration_hrs: float,
459
- account_url: Optional[str] = None
460
- ) -> str:
461
- """
462
- Creates a container and returns a SAS URI with read/write/list
463
- permissions.
464
-
465
- Args:
466
- account_name: str, name of blob storage account
467
- account_key: str, account SAS token or account shared access key
468
- container_name: str, name of container to create, must not match an
469
- existing container in the given storage account
470
- access_duration_hrs: float
471
- account_url: str, optional, defaults to default Azure Storage URL
472
-
473
- Returns: str, URL to newly created container
474
-
475
- Raises: azure.core.exceptions.ResourceExistsError, if container already
476
- exists
477
- """
478
-
479
- if account_url is None:
480
- account_url = build_azure_storage_uri(account=account_name)
481
- with ContainerClient(account_url=account_url,
482
- container_name=container_name,
483
- credential=account_key) as container_client:
484
- container_client.create_container()
485
-
486
- permissions = ContainerSasPermissions(read=True, write=True, list=True)
487
- container_sas_token = generate_container_sas(
488
- account_name=account_name,
489
- container_name=container_name,
490
- account_key=account_key,
491
- permission=permissions,
492
- expiry=datetime.utcnow() + timedelta(hours=access_duration_hrs))
493
-
494
- return f'{account_url}/{container_name}?{container_sas_token}'
495
-
496
-
497
- def split_container_uri(container_uri: str) -> Tuple[str, str, Optional[str]]:
498
- """
499
- Args:
500
- container_uri: str, URI to blob storage container
501
- <account_url>/<container>?<sas_token>
502
-
503
- Returns: account_url, container_name, sas_token
504
- """
505
-
506
- account_container = container_uri.split('?', maxsplit=1)[0]
507
- account_url, container_name = account_container.rsplit('/', maxsplit=1)
508
- sas_token = get_sas_token_from_uri(container_uri)
509
- return account_url, container_name, sas_token
@@ -1,59 +0,0 @@
1
- ########
2
- #
3
- # string_utils.py
4
- #
5
- # Miscellaneous string utilities
6
- #
7
- ########
8
-
9
- import re
10
-
11
- def is_float(s):
12
- """
13
- Checks whether a string represents a valid float
14
- """
15
-
16
- try:
17
- _ = float(s)
18
- except ValueError:
19
- return False
20
- return True
21
-
22
-
23
- def human_readable_to_bytes(size):
24
- """
25
- Given a human-readable byte string (e.g. 2G, 10GB, 30MB, 20KB),
26
- return the number of bytes. Will return 0 if the argument has
27
- unexpected form.
28
-
29
- https://gist.github.com/beugley/ccd69945346759eb6142272a6d69b4e0
30
- """
31
-
32
- size = re.sub(r'\s+', '', size)
33
-
34
- if (size[-1] == 'B'):
35
- size = size[:-1]
36
-
37
- if (size.isdigit()):
38
- bytes = int(size)
39
- elif (is_float(size)):
40
- bytes = float(size)
41
- else:
42
- bytes = size[:-1]
43
- unit = size[-1]
44
- try:
45
- bytes = float(bytes)
46
- if (unit == 'T'):
47
- bytes *= 1024*1024*1024*1024
48
- elif (unit == 'G'):
49
- bytes *= 1024*1024*1024
50
- elif (unit == 'M'):
51
- bytes *= 1024*1024
52
- elif (unit == 'K'):
53
- bytes *= 1024
54
- else:
55
- bytes = 0
56
- except ValueError:
57
- bytes = 0
58
-
59
- return bytes
@@ -1,144 +0,0 @@
1
- ########
2
- #
3
- # url_utils.py
4
- #
5
- # Frequently-used functions for downloading or manipulating URLs
6
- #
7
- ########
8
-
9
- #%% Imports and constants
10
-
11
- import os
12
- import re
13
- import urllib
14
- import tempfile
15
- import requests
16
-
17
- from tqdm import tqdm
18
- from urllib.parse import urlparse
19
-
20
- url_utils_temp_dir = None
21
- max_path_len = 255
22
-
23
-
24
- #%% Download functions
25
-
26
- class DownloadProgressBar():
27
- """
28
- https://stackoverflow.com/questions/37748105/how-to-use-progressbar-module-with-urlretrieve
29
- """
30
-
31
- def __init__(self):
32
- self.pbar = None
33
-
34
- def __call__(self, block_num, block_size, total_size):
35
- if not self.pbar:
36
- # This is a pretty random import I'd rather not depend on outside of the
37
- # rare case where it's used, so importing locally
38
- # pip install progressbar2
39
- import progressbar
40
- self.pbar = progressbar.ProgressBar(max_value=total_size)
41
- self.pbar.start()
42
-
43
- downloaded = block_num * block_size
44
- if downloaded < total_size:
45
- self.pbar.update(downloaded)
46
- else:
47
- self.pbar.finish()
48
-
49
-
50
- def get_temp_folder(preferred_name='url_utils'):
51
-
52
- global url_utils_temp_dir
53
-
54
- if url_utils_temp_dir is None:
55
- url_utils_temp_dir = os.path.join(tempfile.gettempdir(),preferred_name)
56
- os.makedirs(url_utils_temp_dir,exist_ok=True)
57
-
58
- return url_utils_temp_dir
59
-
60
-
61
- def download_url(url, destination_filename=None, progress_updater=None,
62
- force_download=False, verbose=True):
63
- """
64
- Download a URL to a file. If no file is specified, creates a temporary file,
65
- with a semi-best-effort to avoid filename collisions.
66
-
67
- Prints some diagnostic information and makes sure to omit SAS tokens from printouts.
68
-
69
- progress_updater can be "None", "True", or a specific callback.
70
- """
71
-
72
- if progress_updater is not None and isinstance(progress_updater,bool):
73
- if not progress_updater:
74
- progress_updater = None
75
- else:
76
- progress_updater = DownloadProgressBar()
77
-
78
- url_no_sas = url.split('?')[0]
79
-
80
- if destination_filename is None:
81
- target_folder = get_temp_folder()
82
- url_without_sas = url.split('?', 1)[0]
83
-
84
- # This does not guarantee uniqueness, hence "semi-best-effort"
85
- url_as_filename = re.sub(r'\W+', '', url_without_sas)
86
- n_folder_chars = len(url_utils_temp_dir)
87
- if len(url_as_filename) + n_folder_chars > max_path_len:
88
- print('Warning: truncating filename target to {} characters'.format(max_path_len))
89
- url_as_filename = url_as_filename[-1*(max_path_len-n_folder_chars):]
90
- destination_filename = \
91
- os.path.join(target_folder,url_as_filename)
92
-
93
- if (not force_download) and (os.path.isfile(destination_filename)):
94
- if verbose:
95
- print('Bypassing download of already-downloaded file {}'.format(os.path.basename(url_no_sas)))
96
- else:
97
- if verbose:
98
- print('Downloading file {} to {}'.format(os.path.basename(url_no_sas),destination_filename),end='')
99
- target_dir = os.path.dirname(destination_filename)
100
- os.makedirs(target_dir,exist_ok=True)
101
- urllib.request.urlretrieve(url, destination_filename, progress_updater)
102
- assert(os.path.isfile(destination_filename))
103
- nBytes = os.path.getsize(destination_filename)
104
- if verbose:
105
- print('...done, {} bytes.'.format(nBytes))
106
-
107
- return destination_filename
108
-
109
-
110
- def download_relative_filename(url, output_base, verbose=False):
111
- """
112
- Download a URL to output_base, preserving relative path
113
- """
114
-
115
- p = urlparse(url)
116
- # remove the leading '/'
117
- assert p.path.startswith('/'); relative_filename = p.path[1:]
118
- destination_filename = os.path.join(output_base,relative_filename)
119
- download_url(url, destination_filename, verbose=verbose)
120
-
121
-
122
- def test_urls(urls, error_on_failure=True):
123
- """
124
- Verify that a list of URLs is available (returns status 200). By default,
125
- errors if any URL is unavailable. If error_on_failure is False, returns
126
- status codes for each URL.
127
-
128
- TODO: trivially parallelizable.
129
- """
130
-
131
- status_codes = []
132
-
133
- for url in tqdm(urls):
134
-
135
- r = requests.get(url)
136
-
137
- if error_on_failure and r.status_code != 200:
138
- raise ValueError('Could not access {}: error {}'.format(url,r.status_code))
139
- status_codes.append(r.status_code)
140
-
141
- return status_codes
142
-
143
-
144
-