megadetector 5.0.29__py3-none-any.whl → 10.0.0__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.
- megadetector/classification/efficientnet/model.py +8 -8
- megadetector/classification/efficientnet/utils.py +6 -5
- megadetector/classification/prepare_classification_script_mc.py +3 -3
- megadetector/data_management/annotations/annotation_constants.py +0 -1
- megadetector/data_management/camtrap_dp_to_coco.py +34 -1
- megadetector/data_management/cct_json_utils.py +2 -2
- megadetector/data_management/coco_to_yolo.py +22 -5
- megadetector/data_management/databases/add_width_and_height_to_db.py +85 -12
- megadetector/data_management/databases/combine_coco_camera_traps_files.py +2 -2
- megadetector/data_management/databases/integrity_check_json_db.py +29 -15
- megadetector/data_management/generate_crops_from_cct.py +50 -1
- megadetector/data_management/labelme_to_coco.py +4 -2
- megadetector/data_management/labelme_to_yolo.py +82 -2
- megadetector/data_management/lila/generate_lila_per_image_labels.py +276 -18
- megadetector/data_management/lila/get_lila_annotation_counts.py +5 -3
- megadetector/data_management/lila/lila_common.py +3 -0
- megadetector/data_management/lila/test_lila_metadata_urls.py +15 -5
- megadetector/data_management/mewc_to_md.py +5 -0
- megadetector/data_management/ocr_tools.py +4 -3
- megadetector/data_management/read_exif.py +20 -5
- megadetector/data_management/remap_coco_categories.py +66 -4
- megadetector/data_management/remove_exif.py +50 -1
- megadetector/data_management/rename_images.py +3 -3
- megadetector/data_management/resize_coco_dataset.py +563 -95
- megadetector/data_management/yolo_output_to_md_output.py +131 -2
- megadetector/data_management/yolo_to_coco.py +140 -5
- megadetector/detection/change_detection.py +4 -3
- megadetector/detection/pytorch_detector.py +60 -22
- megadetector/detection/run_detector.py +225 -25
- megadetector/detection/run_detector_batch.py +42 -16
- megadetector/detection/run_inference_with_yolov5_val.py +12 -2
- megadetector/detection/run_tiled_inference.py +1 -0
- megadetector/detection/video_utils.py +53 -24
- megadetector/postprocessing/add_max_conf.py +4 -0
- megadetector/postprocessing/categorize_detections_by_size.py +1 -1
- megadetector/postprocessing/classification_postprocessing.py +55 -20
- megadetector/postprocessing/combine_batch_outputs.py +3 -2
- megadetector/postprocessing/compare_batch_results.py +64 -10
- megadetector/postprocessing/convert_output_format.py +12 -8
- megadetector/postprocessing/create_crop_folder.py +137 -10
- megadetector/postprocessing/load_api_results.py +26 -8
- megadetector/postprocessing/md_to_coco.py +4 -4
- megadetector/postprocessing/md_to_labelme.py +18 -7
- megadetector/postprocessing/merge_detections.py +5 -0
- megadetector/postprocessing/postprocess_batch_results.py +6 -3
- megadetector/postprocessing/remap_detection_categories.py +55 -2
- megadetector/postprocessing/render_detection_confusion_matrix.py +9 -6
- megadetector/postprocessing/repeat_detection_elimination/repeat_detections_core.py +2 -2
- megadetector/taxonomy_mapping/map_new_lila_datasets.py +3 -4
- megadetector/taxonomy_mapping/prepare_lila_taxonomy_release.py +40 -19
- megadetector/taxonomy_mapping/preview_lila_taxonomy.py +1 -1
- megadetector/taxonomy_mapping/species_lookup.py +123 -41
- megadetector/utils/ct_utils.py +133 -113
- megadetector/utils/md_tests.py +93 -13
- megadetector/utils/path_utils.py +137 -107
- megadetector/utils/split_locations_into_train_val.py +2 -2
- megadetector/utils/string_utils.py +7 -7
- megadetector/utils/url_utils.py +81 -58
- megadetector/utils/wi_utils.py +46 -17
- megadetector/visualization/plot_utils.py +13 -9
- megadetector/visualization/render_images_with_thumbnails.py +2 -1
- megadetector/visualization/visualization_utils.py +94 -46
- megadetector/visualization/visualize_db.py +36 -9
- megadetector/visualization/visualize_detector_output.py +4 -4
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/METADATA +135 -135
- megadetector-10.0.0.dist-info/RECORD +139 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/licenses/LICENSE +0 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/top_level.txt +0 -0
- megadetector/api/batch_processing/api_core/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/__init__.py +0 -0
- megadetector/api/batch_processing/api_core/batch_service/score.py +0 -438
- megadetector/api/batch_processing/api_core/server.py +0 -294
- megadetector/api/batch_processing/api_core/server_api_config.py +0 -97
- megadetector/api/batch_processing/api_core/server_app_config.py +0 -55
- megadetector/api/batch_processing/api_core/server_batch_job_manager.py +0 -220
- megadetector/api/batch_processing/api_core/server_job_status_table.py +0 -149
- megadetector/api/batch_processing/api_core/server_orchestration.py +0 -360
- megadetector/api/batch_processing/api_core/server_utils.py +0 -88
- megadetector/api/batch_processing/api_core_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_core_support/aggregate_results_manually.py +0 -46
- megadetector/api/batch_processing/api_support/__init__.py +0 -0
- megadetector/api/batch_processing/api_support/summarize_daily_activity.py +0 -152
- megadetector/api/batch_processing/data_preparation/__init__.py +0 -0
- megadetector/api/synchronous/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/__init__.py +0 -0
- megadetector/api/synchronous/api_core/animal_detection_api/api_backend.py +0 -151
- megadetector/api/synchronous/api_core/animal_detection_api/api_frontend.py +0 -263
- megadetector/api/synchronous/api_core/animal_detection_api/config.py +0 -35
- megadetector/api/synchronous/api_core/tests/__init__.py +0 -0
- megadetector/api/synchronous/api_core/tests/load_test.py +0 -109
- megadetector/utils/azure_utils.py +0 -178
- megadetector/utils/sas_blob_utils.py +0 -513
- megadetector-5.0.29.dist-info/RECORD +0 -163
- /megadetector/{api/batch_processing/__init__.py → __init__.py} +0 -0
- {megadetector-5.0.29.dist-info → megadetector-10.0.0.dist-info}/WHEEL +0 -0
megadetector/utils/ct_utils.py
CHANGED
|
@@ -206,9 +206,9 @@ def json_serialize_datetime(obj):
|
|
|
206
206
|
|
|
207
207
|
|
|
208
208
|
def write_json(path,
|
|
209
|
-
content,
|
|
210
|
-
indent=1,
|
|
211
|
-
force_str=False,
|
|
209
|
+
content,
|
|
210
|
+
indent=1,
|
|
211
|
+
force_str=False,
|
|
212
212
|
serialize_datetimes=False,
|
|
213
213
|
ensure_ascii=True,
|
|
214
214
|
encoding='utf-8'):
|
|
@@ -222,6 +222,7 @@ def write_json(path,
|
|
|
222
222
|
force_str (bool, optional): whether to force string conversion for non-serializable objects
|
|
223
223
|
serialize_datetimes (bool, optional): whether to serialize datetime objects to ISO format
|
|
224
224
|
ensure_ascii (bool, optional): whether to ensure ASCII characters in the output
|
|
225
|
+
encoding (str, optional): string encoding to use
|
|
225
226
|
"""
|
|
226
227
|
|
|
227
228
|
default_handler = None
|
|
@@ -238,9 +239,13 @@ def write_json(path,
|
|
|
238
239
|
elif force_str:
|
|
239
240
|
default_handler = str
|
|
240
241
|
|
|
242
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
243
|
+
|
|
241
244
|
with open(path, 'w', newline='\n', encoding=encoding) as f:
|
|
242
245
|
json.dump(content, f, indent=indent, default=default_handler, ensure_ascii=ensure_ascii)
|
|
243
246
|
|
|
247
|
+
# ...def write_json(...)
|
|
248
|
+
|
|
244
249
|
|
|
245
250
|
def convert_yolo_to_xywh(yolo_box):
|
|
246
251
|
"""
|
|
@@ -389,8 +394,8 @@ def point_dist(p1,p2):
|
|
|
389
394
|
Computes the distance between two points, represented as length-two tuples.
|
|
390
395
|
|
|
391
396
|
Args:
|
|
392
|
-
p1: point, formatted as (x,y)
|
|
393
|
-
p2: point, formatted as (x,y)
|
|
397
|
+
p1 (list or tuple): point, formatted as (x,y)
|
|
398
|
+
p2 (list or tuple): point, formatted as (x,y)
|
|
394
399
|
|
|
395
400
|
Returns:
|
|
396
401
|
float: the Euclidean distance between p1 and p2
|
|
@@ -407,8 +412,8 @@ def rect_distance(r1, r2, format='x0y0x1y1'):
|
|
|
407
412
|
Can also specify "format" as x0y0wh for MD-style bbox formatting (x0,y0,w,h).
|
|
408
413
|
|
|
409
414
|
Args:
|
|
410
|
-
r1: rectangle, formatted as (x0,y0,x1,y1) or (x0,y0,xy,y1)
|
|
411
|
-
r2: rectangle, formatted as (x0,y0,x1,y1) or (x0,y0,xy,y1)
|
|
415
|
+
r1 (list or tuple): rectangle, formatted as (x0,y0,x1,y1) or (x0,y0,xy,y1)
|
|
416
|
+
r2 (list or tuple): rectangle, formatted as (x0,y0,x1,y1) or (x0,y0,xy,y1)
|
|
412
417
|
format (str, optional): whether the boxes are formatted as 'x0y0x1y1' (default) or 'x0y0wh'
|
|
413
418
|
|
|
414
419
|
Returns:
|
|
@@ -477,7 +482,7 @@ def split_list_into_n_chunks(L, n, chunk_strategy='greedy'): # noqa
|
|
|
477
482
|
Args:
|
|
478
483
|
L (list): list to split into chunks
|
|
479
484
|
n (int): number of chunks
|
|
480
|
-
chunk_strategy (str,
|
|
485
|
+
chunk_strategy (str, optional): "greedy" or "balanced"; see above
|
|
481
486
|
|
|
482
487
|
Returns:
|
|
483
488
|
list: list of chunks, each of which is a list
|
|
@@ -496,7 +501,7 @@ def split_list_into_n_chunks(L, n, chunk_strategy='greedy'): # noqa
|
|
|
496
501
|
raise ValueError('Invalid chunk strategy: {}'.format(chunk_strategy))
|
|
497
502
|
|
|
498
503
|
|
|
499
|
-
def sort_list_of_dicts_by_key(L,k,reverse=False): # noqa
|
|
504
|
+
def sort_list_of_dicts_by_key(L, k, reverse=False, none_handling='smallest'): # noqa ("L" should be lowercase)
|
|
500
505
|
"""
|
|
501
506
|
Sorts the list of dictionaries [L] by the key [k].
|
|
502
507
|
|
|
@@ -504,11 +509,25 @@ def sort_list_of_dicts_by_key(L,k,reverse=False): # noqa
|
|
|
504
509
|
L (list): list of dictionaries to sort
|
|
505
510
|
k (object, typically str): the sort key
|
|
506
511
|
reverse (bool, optional): whether to sort in reverse (descending) order
|
|
512
|
+
none_handling (str, optional): how to handle None values. Options:
|
|
513
|
+
"smallest" - treat None as smaller than all other values (default)
|
|
514
|
+
"largest" - treat None as larger than all other values
|
|
515
|
+
"error" - raise error when None is compared with non-None
|
|
507
516
|
|
|
508
517
|
Returns:
|
|
509
|
-
|
|
518
|
+
list: sorted copy of [L]
|
|
510
519
|
"""
|
|
511
|
-
|
|
520
|
+
|
|
521
|
+
if none_handling == 'error':
|
|
522
|
+
return sorted(L, key=lambda d: d[k], reverse=reverse)
|
|
523
|
+
elif none_handling == 'smallest':
|
|
524
|
+
# None values treated as smaller than other values: use tuple (is_not_none, value)
|
|
525
|
+
return sorted(L, key=lambda d: (d[k] is not None, d[k]), reverse=reverse)
|
|
526
|
+
elif none_handling == "largest":
|
|
527
|
+
# None values treated as larger than other values: use tuple (is_none, value)
|
|
528
|
+
return sorted(L, key=lambda d: (d[k] is None, d[k]), reverse=reverse)
|
|
529
|
+
else:
|
|
530
|
+
raise ValueError('Invalid none_handling value: {}'.format(none_handling))
|
|
512
531
|
|
|
513
532
|
|
|
514
533
|
def sort_dictionary_by_key(d,reverse=False):
|
|
@@ -573,7 +592,7 @@ def round_floats_in_nested_dict(obj, decimal_places=5, allow_iterator_conversion
|
|
|
573
592
|
sets, and other iterables. Modifies mutable objects in place.
|
|
574
593
|
|
|
575
594
|
Args:
|
|
576
|
-
obj: The object to process (can be a dict, list, set, tuple, or primitive value)
|
|
595
|
+
obj (obj): The object to process (can be a dict, list, set, tuple, or primitive value)
|
|
577
596
|
decimal_places (int, optional): Number of decimal places to round to
|
|
578
597
|
allow_iterator_conversion (bool, optional): for iterator types, should we convert
|
|
579
598
|
to lists? Otherwise we error.
|
|
@@ -595,7 +614,7 @@ def round_floats_in_nested_dict(obj, decimal_places=5, allow_iterator_conversion
|
|
|
595
614
|
|
|
596
615
|
elif isinstance(obj, tuple):
|
|
597
616
|
# Tuples are immutable, so we create a new one
|
|
598
|
-
return tuple(round_floats_in_nested_dict(item, decimal_places=decimal_places,
|
|
617
|
+
return tuple(round_floats_in_nested_dict(item, decimal_places=decimal_places,
|
|
599
618
|
allow_iterator_conversion=allow_iterator_conversion) for item in obj)
|
|
600
619
|
|
|
601
620
|
elif isinstance(obj, set):
|
|
@@ -606,11 +625,11 @@ def round_floats_in_nested_dict(obj, decimal_places=5, allow_iterator_conversion
|
|
|
606
625
|
|
|
607
626
|
elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
|
|
608
627
|
# Handle other iterable types: convert to list, process, and convert back
|
|
609
|
-
processed_list = [round_floats_in_nested_dict(item,
|
|
628
|
+
processed_list = [round_floats_in_nested_dict(item,
|
|
610
629
|
decimal_places=decimal_places,
|
|
611
630
|
allow_iterator_conversion=allow_iterator_conversion) \
|
|
612
631
|
for item in obj]
|
|
613
|
-
|
|
632
|
+
|
|
614
633
|
# Try to recreate the original type, but fall back to list for iterators
|
|
615
634
|
try:
|
|
616
635
|
return type(obj)(processed_list)
|
|
@@ -682,7 +701,7 @@ def is_float(v):
|
|
|
682
701
|
|
|
683
702
|
if v is None:
|
|
684
703
|
return False
|
|
685
|
-
|
|
704
|
+
|
|
686
705
|
try:
|
|
687
706
|
_ = float(v)
|
|
688
707
|
return True
|
|
@@ -714,7 +733,7 @@ def is_empty(v):
|
|
|
714
733
|
data from .csv files. "empty" includes None, '', and NaN.
|
|
715
734
|
|
|
716
735
|
Args:
|
|
717
|
-
v: the object to evaluate for emptiness
|
|
736
|
+
v (obj): the object to evaluate for emptiness
|
|
718
737
|
|
|
719
738
|
Returns:
|
|
720
739
|
bool: True if [v] is None, '', or NaN, otherwise False
|
|
@@ -731,12 +750,12 @@ def is_empty(v):
|
|
|
731
750
|
def to_bool(v):
|
|
732
751
|
"""
|
|
733
752
|
Convert an object to a bool with specific rules.
|
|
734
|
-
|
|
753
|
+
|
|
735
754
|
Args:
|
|
736
|
-
|
|
737
|
-
|
|
755
|
+
v (object): The object to convert
|
|
756
|
+
|
|
738
757
|
Returns:
|
|
739
|
-
bool or None:
|
|
758
|
+
bool or None:
|
|
740
759
|
- For strings: True if 'true' (case-insensitive), False if 'false', recursively applied if int-like
|
|
741
760
|
- For int/bytes: False if 0, True otherwise
|
|
742
761
|
- For bool: returns the bool as-is
|
|
@@ -745,7 +764,7 @@ def to_bool(v):
|
|
|
745
764
|
|
|
746
765
|
if isinstance(v, bool):
|
|
747
766
|
return v
|
|
748
|
-
|
|
767
|
+
|
|
749
768
|
if isinstance(v, str):
|
|
750
769
|
|
|
751
770
|
try:
|
|
@@ -764,7 +783,7 @@ def to_bool(v):
|
|
|
764
783
|
|
|
765
784
|
if isinstance(v, (int, bytes)):
|
|
766
785
|
return v != 0
|
|
767
|
-
|
|
786
|
+
|
|
768
787
|
return None
|
|
769
788
|
|
|
770
789
|
|
|
@@ -817,7 +836,7 @@ def isnan(v):
|
|
|
817
836
|
Returns True if v is a nan-valued float, otherwise returns False.
|
|
818
837
|
|
|
819
838
|
Args:
|
|
820
|
-
v: the object to evaluate for nan-ness
|
|
839
|
+
v (obj): the object to evaluate for nan-ness
|
|
821
840
|
|
|
822
841
|
Returns:
|
|
823
842
|
bool: True if v is a nan-valued float, otherwise False
|
|
@@ -866,7 +885,7 @@ def is_function_name(s,calling_namespace):
|
|
|
866
885
|
def parse_kvp(s,kv_separator='='):
|
|
867
886
|
"""
|
|
868
887
|
Parse a key/value pair, separated by [kv_separator]. Errors if s is not
|
|
869
|
-
a valid key/value pair string. Strips leading/trailing whitespace from
|
|
888
|
+
a valid key/value pair string. Strips leading/trailing whitespace from
|
|
870
889
|
the key and value.
|
|
871
890
|
|
|
872
891
|
Args:
|
|
@@ -1014,6 +1033,10 @@ def make_temp_folder(top_level_folder='megadetector',subfolder=None,append_guid=
|
|
|
1014
1033
|
def make_test_folder(subfolder=None):
|
|
1015
1034
|
"""
|
|
1016
1035
|
Wrapper around make_temp_folder that creates folders within megadetector/tests
|
|
1036
|
+
|
|
1037
|
+
Args:
|
|
1038
|
+
subfolder (str): specific subfolder to create within the default megadetector temp
|
|
1039
|
+
folder.
|
|
1017
1040
|
"""
|
|
1018
1041
|
|
|
1019
1042
|
return make_temp_folder(top_level_folder='megadetector/tests',
|
|
@@ -1135,14 +1158,14 @@ def test_geometric_operations():
|
|
|
1135
1158
|
r1 = [0.4,0.8,10,22]; r2 = [100, 101, 200, 210.4]; assert abs(rect_distance(r1,r2)-119.753) < 0.001
|
|
1136
1159
|
r1 = [0.4,0.8,10,22]; r2 = [101, 101, 200, 210.4]; assert abs(rect_distance(r1,r2)-120.507) < 0.001
|
|
1137
1160
|
r1 = [0.4,0.8,10,22]; r2 = [120, 120, 200, 210.4]; assert abs(rect_distance(r1,r2)-147.323) < 0.001
|
|
1138
|
-
|
|
1161
|
+
|
|
1139
1162
|
# Test with 'x0y0wh' format
|
|
1140
1163
|
r1_wh = [0,0,1,1]; r2_wh = [1,0,1,1]; assert rect_distance(r1_wh, r2_wh, format='x0y0wh') == 0
|
|
1141
1164
|
r1_wh = [0,0,1,1]; r2_wh = [1.5,0,1,1]; assert abs(rect_distance(r1_wh, r2_wh, format='x0y0wh') - 0.5) < 0.00001
|
|
1142
1165
|
|
|
1143
1166
|
|
|
1144
1167
|
##%% Test point_dist
|
|
1145
|
-
|
|
1168
|
+
|
|
1146
1169
|
assert point_dist((0,0), (3,4)) == 5.0
|
|
1147
1170
|
assert point_dist((1,1), (1,1)) == 0.0
|
|
1148
1171
|
|
|
@@ -1153,17 +1176,17 @@ def test_dictionary_operations():
|
|
|
1153
1176
|
"""
|
|
1154
1177
|
|
|
1155
1178
|
##%% Test sort_list_of_dicts_by_key
|
|
1156
|
-
|
|
1157
|
-
|
|
1179
|
+
|
|
1180
|
+
x = [{'a':5},{'a':0},{'a':10}]
|
|
1158
1181
|
k = 'a'
|
|
1159
|
-
|
|
1160
|
-
assert
|
|
1161
|
-
|
|
1162
|
-
assert
|
|
1182
|
+
sorted_x = sort_list_of_dicts_by_key(x, k)
|
|
1183
|
+
assert sorted_x[0]['a'] == 0; assert sorted_x[1]['a'] == 5; assert sorted_x[2]['a'] == 10
|
|
1184
|
+
sorted_x_rev = sort_list_of_dicts_by_key(x, k, reverse=True)
|
|
1185
|
+
assert sorted_x_rev[0]['a'] == 10; assert sorted_x_rev[1]['a'] == 5; assert sorted_x_rev[2]['a'] == 0
|
|
1163
1186
|
|
|
1164
1187
|
|
|
1165
1188
|
##%% Test sort_dictionary_by_key
|
|
1166
|
-
|
|
1189
|
+
|
|
1167
1190
|
d_key = {'b': 2, 'a': 1, 'c': 3}
|
|
1168
1191
|
sorted_d_key = sort_dictionary_by_key(d_key)
|
|
1169
1192
|
assert list(sorted_d_key.keys()) == ['a', 'b', 'c']
|
|
@@ -1190,7 +1213,7 @@ def test_dictionary_operations():
|
|
|
1190
1213
|
d_inv = {'a': 'x', 'b': 'y'}
|
|
1191
1214
|
inverted_d = invert_dictionary(d_inv)
|
|
1192
1215
|
assert inverted_d == {'x': 'a', 'y': 'b'}
|
|
1193
|
-
|
|
1216
|
+
|
|
1194
1217
|
# Does not check for uniqueness, last one wins
|
|
1195
1218
|
d_inv_dup = {'a': 'x', 'b': 'x'}
|
|
1196
1219
|
inverted_d_dup = invert_dictionary(d_inv_dup)
|
|
@@ -1203,7 +1226,7 @@ def test_float_rounding_and_truncation():
|
|
|
1203
1226
|
"""
|
|
1204
1227
|
|
|
1205
1228
|
##%% Test round_floats_in_nested_dict
|
|
1206
|
-
|
|
1229
|
+
|
|
1207
1230
|
data = {
|
|
1208
1231
|
"name": "Project X",
|
|
1209
1232
|
"values": [1.23456789, 2.3456789],
|
|
@@ -1229,7 +1252,7 @@ def test_float_rounding_and_truncation():
|
|
|
1229
1252
|
|
|
1230
1253
|
|
|
1231
1254
|
##%% Test truncate_float_array and truncate_float
|
|
1232
|
-
|
|
1255
|
+
|
|
1233
1256
|
assert truncate_float_array([0.12345, 0.67890], precision=3) == [0.123, 0.678]
|
|
1234
1257
|
assert truncate_float_array([1.0, 2.0], precision=2) == [1.0, 2.0]
|
|
1235
1258
|
assert truncate_float(0.12345, precision=3) == 0.123
|
|
@@ -1239,7 +1262,7 @@ def test_float_rounding_and_truncation():
|
|
|
1239
1262
|
|
|
1240
1263
|
|
|
1241
1264
|
##%% Test round_float_array and round_float
|
|
1242
|
-
|
|
1265
|
+
|
|
1243
1266
|
assert round_float_array([0.12345, 0.67890], precision=3) == [0.123, 0.679]
|
|
1244
1267
|
assert round_float_array([1.0, 2.0], precision=2) == [1.0, 2.0]
|
|
1245
1268
|
assert round_float(0.12345, precision=3) == 0.123
|
|
@@ -1253,7 +1276,7 @@ def test_object_conversion_and_presentation():
|
|
|
1253
1276
|
"""
|
|
1254
1277
|
|
|
1255
1278
|
##%% Test args_to_object
|
|
1256
|
-
|
|
1279
|
+
|
|
1257
1280
|
class ArgsObject:
|
|
1258
1281
|
pass
|
|
1259
1282
|
args_namespace = type('ArgsNameSpace', (), {'a': 1, 'b': 'test', '_c': 'ignored'})
|
|
@@ -1265,7 +1288,7 @@ def test_object_conversion_and_presentation():
|
|
|
1265
1288
|
|
|
1266
1289
|
|
|
1267
1290
|
##%% Test dict_to_object
|
|
1268
|
-
|
|
1291
|
+
|
|
1269
1292
|
class DictObject:
|
|
1270
1293
|
pass
|
|
1271
1294
|
d = {'a': 1, 'b': 'test', '_c': 'ignored'}
|
|
@@ -1277,7 +1300,7 @@ def test_object_conversion_and_presentation():
|
|
|
1277
1300
|
|
|
1278
1301
|
|
|
1279
1302
|
##%% Test pretty_print_object
|
|
1280
|
-
|
|
1303
|
+
|
|
1281
1304
|
class PrettyPrintable:
|
|
1282
1305
|
def __init__(self):
|
|
1283
1306
|
self.a = 1
|
|
@@ -1289,7 +1312,7 @@ def test_object_conversion_and_presentation():
|
|
|
1289
1312
|
parsed_json = json.loads(json_str) # Relies on json.loads
|
|
1290
1313
|
assert parsed_json['a'] == 1
|
|
1291
1314
|
assert parsed_json['b'] == "test"
|
|
1292
|
-
|
|
1315
|
+
|
|
1293
1316
|
|
|
1294
1317
|
def test_list_operations():
|
|
1295
1318
|
"""
|
|
@@ -1297,7 +1320,7 @@ def test_list_operations():
|
|
|
1297
1320
|
"""
|
|
1298
1321
|
|
|
1299
1322
|
##%% Test is_list_sorted
|
|
1300
|
-
|
|
1323
|
+
|
|
1301
1324
|
assert is_list_sorted([1, 2, 3])
|
|
1302
1325
|
assert not is_list_sorted([1, 3, 2])
|
|
1303
1326
|
assert is_list_sorted([3, 2, 1], reverse=True)
|
|
@@ -1306,10 +1329,10 @@ def test_list_operations():
|
|
|
1306
1329
|
assert is_list_sorted([1]) # Single element list is sorted
|
|
1307
1330
|
assert is_list_sorted([1,1,1])
|
|
1308
1331
|
assert is_list_sorted([1,1,1], reverse=True)
|
|
1309
|
-
|
|
1310
|
-
|
|
1332
|
+
|
|
1333
|
+
|
|
1311
1334
|
##%% Test split_list_into_fixed_size_chunks
|
|
1312
|
-
|
|
1335
|
+
|
|
1313
1336
|
assert split_list_into_fixed_size_chunks([1,2,3,4,5,6], 2) == [[1,2],[3,4],[5,6]]
|
|
1314
1337
|
assert split_list_into_fixed_size_chunks([1,2,3,4,5], 2) == [[1,2],[3,4],[5]]
|
|
1315
1338
|
assert split_list_into_fixed_size_chunks([], 3) == []
|
|
@@ -1317,11 +1340,11 @@ def test_list_operations():
|
|
|
1317
1340
|
|
|
1318
1341
|
|
|
1319
1342
|
##%% Test split_list_into_n_chunks
|
|
1320
|
-
|
|
1343
|
+
|
|
1321
1344
|
# Greedy
|
|
1322
1345
|
assert split_list_into_n_chunks([1,2,3,4,5,6], 3, chunk_strategy='greedy') == [[1,2],[3,4],[5,6]]
|
|
1323
|
-
assert split_list_into_n_chunks([1,2,3,4,5], 3, chunk_strategy='greedy') == [[1,2],[3,4],[5]]
|
|
1324
|
-
assert split_list_into_n_chunks([1,2,3,4,5,6,7], 3, chunk_strategy='greedy') == [[1,2,3],[4,5],[6,7]]
|
|
1346
|
+
assert split_list_into_n_chunks([1,2,3,4,5], 3, chunk_strategy='greedy') == [[1,2],[3,4],[5]]
|
|
1347
|
+
assert split_list_into_n_chunks([1,2,3,4,5,6,7], 3, chunk_strategy='greedy') == [[1,2,3],[4,5],[6,7]]
|
|
1325
1348
|
assert split_list_into_n_chunks([], 3) == [[],[],[]]
|
|
1326
1349
|
|
|
1327
1350
|
# Balanced
|
|
@@ -1330,7 +1353,7 @@ def test_list_operations():
|
|
|
1330
1353
|
assert split_list_into_n_chunks([], 3, chunk_strategy='balanced') == [[],[],[]]
|
|
1331
1354
|
try:
|
|
1332
1355
|
split_list_into_n_chunks([1,2,3], 2, chunk_strategy='invalid')
|
|
1333
|
-
|
|
1356
|
+
raise AssertionError("ValueError not raised for invalid chunk_strategy")
|
|
1334
1357
|
except ValueError:
|
|
1335
1358
|
pass
|
|
1336
1359
|
|
|
@@ -1341,19 +1364,19 @@ def test_datetime_serialization():
|
|
|
1341
1364
|
"""
|
|
1342
1365
|
|
|
1343
1366
|
##%% Test json_serialize_datetime
|
|
1344
|
-
|
|
1367
|
+
|
|
1345
1368
|
now = datetime.datetime.now()
|
|
1346
1369
|
today = datetime.date.today()
|
|
1347
1370
|
assert json_serialize_datetime(now) == now.isoformat()
|
|
1348
1371
|
assert json_serialize_datetime(today) == today.isoformat()
|
|
1349
1372
|
try:
|
|
1350
1373
|
json_serialize_datetime("not a datetime")
|
|
1351
|
-
|
|
1374
|
+
raise AssertionError("TypeError not raised for non-datetime object")
|
|
1352
1375
|
except TypeError:
|
|
1353
1376
|
pass
|
|
1354
1377
|
try:
|
|
1355
1378
|
json_serialize_datetime(123)
|
|
1356
|
-
|
|
1379
|
+
raise AssertionError("TypeError not raised for non-datetime object")
|
|
1357
1380
|
except TypeError:
|
|
1358
1381
|
pass
|
|
1359
1382
|
|
|
@@ -1364,7 +1387,7 @@ def test_bounding_box_operations():
|
|
|
1364
1387
|
"""
|
|
1365
1388
|
|
|
1366
1389
|
##%% Test convert_yolo_to_xywh
|
|
1367
|
-
|
|
1390
|
+
|
|
1368
1391
|
# [x_center, y_center, w, h]
|
|
1369
1392
|
yolo_box = [0.5, 0.5, 0.2, 0.2]
|
|
1370
1393
|
# [x_min, y_min, width_of_box, height_of_box]
|
|
@@ -1373,7 +1396,7 @@ def test_bounding_box_operations():
|
|
|
1373
1396
|
|
|
1374
1397
|
|
|
1375
1398
|
##%% Test convert_xywh_to_xyxy
|
|
1376
|
-
|
|
1399
|
+
|
|
1377
1400
|
# [x_min, y_min, width_of_box, height_of_box]
|
|
1378
1401
|
xywh_box = [0.1, 0.1, 0.3, 0.3]
|
|
1379
1402
|
# [x_min, y_min, x_max, y_max]
|
|
@@ -1387,7 +1410,7 @@ def test_bounding_box_operations():
|
|
|
1387
1410
|
bb2 = [0.25, 0.25, 0.5, 0.5]
|
|
1388
1411
|
assert abs(get_iou(bb1, bb2) - 0.142857) < 1e-5
|
|
1389
1412
|
bb3 = [0, 0, 1, 1]
|
|
1390
|
-
bb4 = [0.5, 0.5, 1, 1]
|
|
1413
|
+
bb4 = [0.5, 0.5, 1, 1]
|
|
1391
1414
|
assert abs(get_iou(bb3, bb4) - (0.25 / 1.75)) < 1e-5
|
|
1392
1415
|
bb5 = [0,0,1,1]
|
|
1393
1416
|
bb6 = [1,1,1,1] # No overlap
|
|
@@ -1410,7 +1433,7 @@ def test_detection_processing():
|
|
|
1410
1433
|
"""
|
|
1411
1434
|
|
|
1412
1435
|
##%% Test _get_max_conf_from_detections and get_max_conf
|
|
1413
|
-
|
|
1436
|
+
|
|
1414
1437
|
detections1 = [{'conf': 0.8}, {'conf': 0.9}, {'conf': 0.75}]
|
|
1415
1438
|
assert _get_max_conf_from_detections(detections1) == 0.9
|
|
1416
1439
|
assert _get_max_conf_from_detections([]) == 0.0
|
|
@@ -1424,8 +1447,8 @@ def test_detection_processing():
|
|
|
1424
1447
|
assert get_max_conf(im3) == 0.0
|
|
1425
1448
|
im4 = {'detections': None}
|
|
1426
1449
|
assert get_max_conf(im4) == 0.0
|
|
1427
|
-
|
|
1428
|
-
|
|
1450
|
+
|
|
1451
|
+
|
|
1429
1452
|
##%% Test sort_results_for_image
|
|
1430
1453
|
|
|
1431
1454
|
img_data = {
|
|
@@ -1436,30 +1459,30 @@ def test_detection_processing():
|
|
|
1436
1459
|
]
|
|
1437
1460
|
}
|
|
1438
1461
|
sort_results_for_image(img_data)
|
|
1439
|
-
|
|
1440
|
-
|
|
1462
|
+
|
|
1463
|
+
# Check detections sorted by conf
|
|
1441
1464
|
assert img_data['detections'][0]['conf'] == 0.9
|
|
1442
1465
|
assert img_data['detections'][1]['conf'] == 0.8
|
|
1443
1466
|
assert img_data['detections'][2]['conf'] == 0.7
|
|
1444
|
-
|
|
1445
|
-
|
|
1467
|
+
|
|
1468
|
+
# Check classifications sorted by conf (only for the first original detection, now at index 0 after sort)
|
|
1446
1469
|
assert img_data['detections'][0]['classifications'][0] == ('x', 0.95)
|
|
1447
1470
|
assert img_data['detections'][0]['classifications'][1] == ('y', 0.85)
|
|
1448
|
-
|
|
1449
|
-
|
|
1471
|
+
|
|
1472
|
+
# Check classifications for the second original detection (now at index 2)
|
|
1450
1473
|
assert img_data['detections'][2]['classifications'][0] == ('a', 0.9)
|
|
1451
1474
|
assert img_data['detections'][2]['classifications'][1] == ('b', 0.8)
|
|
1452
1475
|
assert img_data['detections'][2]['classifications'][2] == ('c', 0.6)
|
|
1453
|
-
|
|
1476
|
+
|
|
1454
1477
|
# Test with no detections or no classifications field
|
|
1455
1478
|
img_data_no_det = {'detections': None}
|
|
1456
|
-
sort_results_for_image(img_data_no_det)
|
|
1479
|
+
sort_results_for_image(img_data_no_det)
|
|
1457
1480
|
assert img_data_no_det['detections'] is None
|
|
1458
|
-
|
|
1481
|
+
|
|
1459
1482
|
img_data_empty_det = {'detections': []}
|
|
1460
1483
|
sort_results_for_image(img_data_empty_det)
|
|
1461
1484
|
assert img_data_empty_det['detections'] == []
|
|
1462
|
-
|
|
1485
|
+
|
|
1463
1486
|
img_data_no_classifications_field = {'detections': [{'conf': 0.8}]}
|
|
1464
1487
|
sort_results_for_image(img_data_no_classifications_field)
|
|
1465
1488
|
assert 'classifications' not in img_data_no_classifications_field['detections'][0]
|
|
@@ -1479,7 +1502,7 @@ def test_type_checking_and_validation():
|
|
|
1479
1502
|
"""
|
|
1480
1503
|
|
|
1481
1504
|
##%% Test is_float
|
|
1482
|
-
|
|
1505
|
+
|
|
1483
1506
|
assert is_float(1.23)
|
|
1484
1507
|
assert is_float("1.23")
|
|
1485
1508
|
assert is_float("-1.23")
|
|
@@ -1490,7 +1513,7 @@ def test_type_checking_and_validation():
|
|
|
1490
1513
|
|
|
1491
1514
|
|
|
1492
1515
|
##%% Test is_iterable
|
|
1493
|
-
|
|
1516
|
+
|
|
1494
1517
|
assert is_iterable([1,2,3])
|
|
1495
1518
|
assert is_iterable("hello")
|
|
1496
1519
|
assert is_iterable({'a':1})
|
|
@@ -1501,7 +1524,7 @@ def test_type_checking_and_validation():
|
|
|
1501
1524
|
|
|
1502
1525
|
|
|
1503
1526
|
##%% Test is_empty
|
|
1504
|
-
|
|
1527
|
+
|
|
1505
1528
|
assert is_empty(None)
|
|
1506
1529
|
assert is_empty("")
|
|
1507
1530
|
assert is_empty(np.nan)
|
|
@@ -1513,7 +1536,7 @@ def test_type_checking_and_validation():
|
|
|
1513
1536
|
|
|
1514
1537
|
|
|
1515
1538
|
##%% Test min_none and max_none
|
|
1516
|
-
|
|
1539
|
+
|
|
1517
1540
|
assert min_none(1, 2) == 1
|
|
1518
1541
|
assert min_none(None, 2) == 2
|
|
1519
1542
|
assert min_none(1, None) == 1
|
|
@@ -1525,7 +1548,7 @@ def test_type_checking_and_validation():
|
|
|
1525
1548
|
|
|
1526
1549
|
|
|
1527
1550
|
##%% Test isnan
|
|
1528
|
-
|
|
1551
|
+
|
|
1529
1552
|
assert isnan(np.nan)
|
|
1530
1553
|
assert not isnan(0.0)
|
|
1531
1554
|
assert not isnan("text")
|
|
@@ -1535,7 +1558,7 @@ def test_type_checking_and_validation():
|
|
|
1535
1558
|
|
|
1536
1559
|
|
|
1537
1560
|
##%% Test sets_overlap
|
|
1538
|
-
|
|
1561
|
+
|
|
1539
1562
|
assert sets_overlap({1,2,3}, {3,4,5})
|
|
1540
1563
|
assert not sets_overlap({1,2}, {3,4})
|
|
1541
1564
|
assert sets_overlap([1,2,3], [3,4,5]) # Test with lists
|
|
@@ -1544,7 +1567,7 @@ def test_type_checking_and_validation():
|
|
|
1544
1567
|
|
|
1545
1568
|
|
|
1546
1569
|
##%% Test is_function_name
|
|
1547
|
-
|
|
1570
|
+
|
|
1548
1571
|
def _test_local_func(): pass
|
|
1549
1572
|
assert is_function_name("is_float", locals()) # Test a function in ct_utils
|
|
1550
1573
|
assert is_function_name("_test_local_func", locals()) # Test a local function
|
|
@@ -1563,38 +1586,38 @@ def test_string_parsing():
|
|
|
1563
1586
|
"""
|
|
1564
1587
|
|
|
1565
1588
|
##%% Test parse_kvp and parse_kvp_list
|
|
1566
|
-
|
|
1589
|
+
|
|
1567
1590
|
assert parse_kvp("key=value") == ("key", "value")
|
|
1568
1591
|
assert parse_kvp("key = value with spaces") == ("key", "value with spaces")
|
|
1569
1592
|
assert parse_kvp("key=value1=value2", kv_separator='=') == ("key", "value1=value2")
|
|
1570
1593
|
try:
|
|
1571
1594
|
parse_kvp("keyvalue")
|
|
1572
|
-
|
|
1595
|
+
raise AssertionError("AssertionError not raised for invalid KVP")
|
|
1573
1596
|
except AssertionError:
|
|
1574
1597
|
pass
|
|
1575
|
-
|
|
1598
|
+
|
|
1576
1599
|
kvp_list = ["a=1", "b = 2", "c=foo=bar"]
|
|
1577
1600
|
parsed_list = parse_kvp_list(kvp_list)
|
|
1578
1601
|
assert parsed_list == {"a": "1", "b": "2", "c": "foo=bar"}
|
|
1579
1602
|
assert parse_kvp_list(None) == {}
|
|
1580
1603
|
assert parse_kvp_list([]) == {}
|
|
1581
1604
|
d_initial = {'z': '0'}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
parse_kvp_list(kvp_list, d=d_initial)
|
|
1605
|
+
|
|
1606
|
+
# parse_kvp_list modifies d in place if provided
|
|
1607
|
+
parse_kvp_list(kvp_list, d=d_initial)
|
|
1585
1608
|
assert d_initial == {"z": "0", "a": "1", "b": "2", "c": "foo=bar"}
|
|
1586
|
-
|
|
1587
|
-
|
|
1609
|
+
|
|
1610
|
+
# Test with a different separator
|
|
1588
1611
|
assert parse_kvp("key:value", kv_separator=":") == ("key", "value")
|
|
1589
1612
|
assert parse_kvp_list(["a:1","b:2"], kv_separator=":") == {"a":"1", "b":"2"}
|
|
1590
1613
|
|
|
1591
1614
|
|
|
1592
1615
|
##%% Test dict_to_kvp_list
|
|
1593
|
-
|
|
1616
|
+
|
|
1594
1617
|
d_kvp = {"a": "1", "b": "dog", "c": "foo=bar"}
|
|
1595
1618
|
kvp_str = dict_to_kvp_list(d_kvp)
|
|
1596
|
-
|
|
1597
|
-
|
|
1619
|
+
|
|
1620
|
+
# Order isn't guaranteed, so check for presence of all items and length
|
|
1598
1621
|
assert "a=1" in kvp_str
|
|
1599
1622
|
assert "b=dog" in kvp_str
|
|
1600
1623
|
assert "c=foo=bar" in kvp_str
|
|
@@ -1605,12 +1628,12 @@ def test_string_parsing():
|
|
|
1605
1628
|
d_kvp_int = {"a":1, "b":"text"}
|
|
1606
1629
|
try:
|
|
1607
1630
|
dict_to_kvp_list(d_kvp_int, non_string_value_handling='error')
|
|
1608
|
-
|
|
1631
|
+
raise AssertionError("ValueError not raised for non-string value with 'error' handling")
|
|
1609
1632
|
except ValueError:
|
|
1610
1633
|
pass
|
|
1611
1634
|
convert_result = dict_to_kvp_list(d_kvp_int, non_string_value_handling='convert')
|
|
1612
1635
|
assert "a=1" in convert_result and "b=text" in convert_result
|
|
1613
|
-
|
|
1636
|
+
|
|
1614
1637
|
omit_result = dict_to_kvp_list({"a":1, "b":"text"}, non_string_value_handling='omit')
|
|
1615
1638
|
assert "a=1" not in omit_result and "b=text" in omit_result
|
|
1616
1639
|
assert omit_result == "b=text"
|
|
@@ -1619,7 +1642,7 @@ def test_string_parsing():
|
|
|
1619
1642
|
|
|
1620
1643
|
|
|
1621
1644
|
##%% Test parse_bool_string
|
|
1622
|
-
|
|
1645
|
+
|
|
1623
1646
|
assert parse_bool_string("true")
|
|
1624
1647
|
assert parse_bool_string("True")
|
|
1625
1648
|
assert parse_bool_string(" TRUE ")
|
|
@@ -1630,12 +1653,12 @@ def test_string_parsing():
|
|
|
1630
1653
|
assert parse_bool_string(False) is False
|
|
1631
1654
|
try:
|
|
1632
1655
|
parse_bool_string("maybe")
|
|
1633
|
-
|
|
1656
|
+
raise AssertionError("ValueError not raised for invalid bool string")
|
|
1634
1657
|
except ValueError:
|
|
1635
1658
|
pass
|
|
1636
1659
|
try:
|
|
1637
1660
|
parse_bool_string("1") # Should not parse to True
|
|
1638
|
-
|
|
1661
|
+
raise AssertionError("ValueError not raised for '1'")
|
|
1639
1662
|
except ValueError:
|
|
1640
1663
|
pass
|
|
1641
1664
|
|
|
@@ -1647,18 +1670,18 @@ def test_temp_folder_creation():
|
|
|
1647
1670
|
|
|
1648
1671
|
# Store original tempdir for restoration if modified by tests (though unlikely for make_temp_folder)
|
|
1649
1672
|
original_tempdir = tempfile.gettempdir()
|
|
1650
|
-
|
|
1673
|
+
|
|
1651
1674
|
# Test make_temp_folder
|
|
1652
1675
|
custom_top_level = "my_custom_temp_app_test" # Unique name for this test run
|
|
1653
1676
|
custom_subfolder = "specific_test_run"
|
|
1654
|
-
|
|
1677
|
+
|
|
1655
1678
|
# Test with default subfolder (UUID)
|
|
1656
1679
|
temp_folder1_base = os.path.join(tempfile.gettempdir(), custom_top_level)
|
|
1657
1680
|
temp_folder1 = make_temp_folder(top_level_folder=custom_top_level)
|
|
1658
1681
|
assert os.path.exists(temp_folder1)
|
|
1659
1682
|
assert os.path.basename(os.path.dirname(temp_folder1)) == custom_top_level
|
|
1660
1683
|
assert temp_folder1_base == os.path.dirname(temp_folder1) # Path up to UUID should match
|
|
1661
|
-
|
|
1684
|
+
|
|
1662
1685
|
# Cleanup: remove the custom_top_level which contains the UUID folder
|
|
1663
1686
|
if os.path.exists(temp_folder1_base):
|
|
1664
1687
|
shutil.rmtree(temp_folder1_base)
|
|
@@ -1674,26 +1697,26 @@ def test_temp_folder_creation():
|
|
|
1674
1697
|
assert os.path.basename(temp_folder2) == custom_subfolder
|
|
1675
1698
|
assert os.path.basename(os.path.dirname(temp_folder2)) == custom_top_level
|
|
1676
1699
|
assert temp_folder2 == os.path.join(tempfile.gettempdir(), custom_top_level, custom_subfolder)
|
|
1677
|
-
|
|
1678
|
-
|
|
1700
|
+
|
|
1701
|
+
# Cleanup
|
|
1679
1702
|
if os.path.exists(temp_folder2_base):
|
|
1680
1703
|
shutil.rmtree(temp_folder2_base)
|
|
1681
1704
|
assert not os.path.exists(temp_folder2_base)
|
|
1682
1705
|
|
|
1683
1706
|
|
|
1684
1707
|
# Test make_test_folder (which uses 'megadetector/tests' as top_level)
|
|
1685
|
-
|
|
1708
|
+
#
|
|
1686
1709
|
# This will create tempfile.gettempdir()/megadetector/tests/some_uuid or specified_subfolder
|
|
1687
1710
|
megadetector_temp_base = os.path.join(tempfile.gettempdir(), "megadetector")
|
|
1688
1711
|
test_subfolder = "my_specific_module_test"
|
|
1689
|
-
|
|
1712
|
+
|
|
1690
1713
|
# Test with default subfolder for make_test_folder
|
|
1691
1714
|
test_folder1 = make_test_folder() # Creates megadetector/tests/uuid_folder
|
|
1692
1715
|
assert os.path.exists(test_folder1)
|
|
1693
1716
|
assert os.path.basename(os.path.dirname(test_folder1)) == "tests"
|
|
1694
1717
|
assert os.path.basename(os.path.dirname(os.path.dirname(test_folder1))) == "megadetector"
|
|
1695
|
-
|
|
1696
|
-
|
|
1718
|
+
|
|
1719
|
+
# Cleanup for make_test_folder default: remove the 'megadetector' base temp dir
|
|
1697
1720
|
if os.path.exists(megadetector_temp_base):
|
|
1698
1721
|
shutil.rmtree(megadetector_temp_base)
|
|
1699
1722
|
assert not os.path.exists(megadetector_temp_base)
|
|
@@ -1703,21 +1726,21 @@ def test_temp_folder_creation():
|
|
|
1703
1726
|
test_folder2 = make_test_folder(subfolder=test_subfolder) # megadetector/tests/my_specific_module_test
|
|
1704
1727
|
assert os.path.exists(test_folder2)
|
|
1705
1728
|
assert test_subfolder in test_folder2
|
|
1706
|
-
assert "megadetector" in test_folder2
|
|
1707
|
-
|
|
1708
|
-
|
|
1729
|
+
assert "megadetector" in test_folder2
|
|
1730
|
+
|
|
1731
|
+
# Cleanup for make_test_folder specific: remove the 'megadetector' base temp dir
|
|
1709
1732
|
if os.path.exists(megadetector_temp_base):
|
|
1710
1733
|
shutil.rmtree(megadetector_temp_base)
|
|
1711
1734
|
assert not os.path.exists(megadetector_temp_base)
|
|
1712
|
-
|
|
1735
|
+
|
|
1713
1736
|
# Verify cleanup if top level folder was 'megadetector' (default for make_temp_folder)
|
|
1714
|
-
|
|
1737
|
+
#
|
|
1715
1738
|
# This means it creates tempfile.gettempdir()/megadetector/uuid_folder
|
|
1716
|
-
default_temp_folder = make_temp_folder()
|
|
1739
|
+
default_temp_folder = make_temp_folder()
|
|
1717
1740
|
assert os.path.exists(default_temp_folder)
|
|
1718
1741
|
assert os.path.basename(os.path.dirname(default_temp_folder)) == "megadetector"
|
|
1719
|
-
|
|
1720
|
-
|
|
1742
|
+
|
|
1743
|
+
# Cleanup: remove the 'megadetector' base temp dir created by default make_temp_folder
|
|
1721
1744
|
if os.path.exists(megadetector_temp_base):
|
|
1722
1745
|
shutil.rmtree(megadetector_temp_base)
|
|
1723
1746
|
assert not os.path.exists(megadetector_temp_base)
|
|
@@ -1729,7 +1752,7 @@ def test_temp_folder_creation():
|
|
|
1729
1752
|
def run_all_module_tests():
|
|
1730
1753
|
"""
|
|
1731
1754
|
Run all tests in the ct_utils module. This is not invoked by pytest; this is
|
|
1732
|
-
just a convenience wrapper for
|
|
1755
|
+
just a convenience wrapper for debugging the tests.
|
|
1733
1756
|
"""
|
|
1734
1757
|
|
|
1735
1758
|
test_write_json()
|
|
@@ -1745,6 +1768,3 @@ def run_all_module_tests():
|
|
|
1745
1768
|
test_type_checking_and_validation()
|
|
1746
1769
|
test_string_parsing()
|
|
1747
1770
|
test_temp_folder_creation()
|
|
1748
|
-
|
|
1749
|
-
# from IPython import embed; embed()
|
|
1750
|
-
# run_all_module_tests()
|