dgenerate-ultralytics-headless 8.3.137__py3-none-any.whl → 8.3.224__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.
Files changed (215) hide show
  1. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/METADATA +41 -34
  2. dgenerate_ultralytics_headless-8.3.224.dist-info/RECORD +285 -0
  3. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/WHEEL +1 -1
  4. tests/__init__.py +7 -6
  5. tests/conftest.py +15 -39
  6. tests/test_cli.py +17 -17
  7. tests/test_cuda.py +17 -8
  8. tests/test_engine.py +36 -10
  9. tests/test_exports.py +98 -37
  10. tests/test_integrations.py +12 -15
  11. tests/test_python.py +126 -82
  12. tests/test_solutions.py +319 -135
  13. ultralytics/__init__.py +27 -9
  14. ultralytics/cfg/__init__.py +83 -87
  15. ultralytics/cfg/datasets/Argoverse.yaml +4 -4
  16. ultralytics/cfg/datasets/DOTAv1.5.yaml +2 -2
  17. ultralytics/cfg/datasets/DOTAv1.yaml +2 -2
  18. ultralytics/cfg/datasets/GlobalWheat2020.yaml +2 -2
  19. ultralytics/cfg/datasets/HomeObjects-3K.yaml +4 -5
  20. ultralytics/cfg/datasets/ImageNet.yaml +3 -3
  21. ultralytics/cfg/datasets/Objects365.yaml +24 -20
  22. ultralytics/cfg/datasets/SKU-110K.yaml +9 -9
  23. ultralytics/cfg/datasets/VOC.yaml +10 -13
  24. ultralytics/cfg/datasets/VisDrone.yaml +43 -33
  25. ultralytics/cfg/datasets/african-wildlife.yaml +5 -5
  26. ultralytics/cfg/datasets/brain-tumor.yaml +4 -5
  27. ultralytics/cfg/datasets/carparts-seg.yaml +5 -5
  28. ultralytics/cfg/datasets/coco-pose.yaml +26 -4
  29. ultralytics/cfg/datasets/coco.yaml +4 -4
  30. ultralytics/cfg/datasets/coco128-seg.yaml +2 -2
  31. ultralytics/cfg/datasets/coco128.yaml +2 -2
  32. ultralytics/cfg/datasets/coco8-grayscale.yaml +103 -0
  33. ultralytics/cfg/datasets/coco8-multispectral.yaml +2 -2
  34. ultralytics/cfg/datasets/coco8-pose.yaml +23 -2
  35. ultralytics/cfg/datasets/coco8-seg.yaml +2 -2
  36. ultralytics/cfg/datasets/coco8.yaml +2 -2
  37. ultralytics/cfg/datasets/construction-ppe.yaml +32 -0
  38. ultralytics/cfg/datasets/crack-seg.yaml +5 -5
  39. ultralytics/cfg/datasets/dog-pose.yaml +32 -4
  40. ultralytics/cfg/datasets/dota8-multispectral.yaml +2 -2
  41. ultralytics/cfg/datasets/dota8.yaml +2 -2
  42. ultralytics/cfg/datasets/hand-keypoints.yaml +29 -4
  43. ultralytics/cfg/datasets/lvis.yaml +9 -9
  44. ultralytics/cfg/datasets/medical-pills.yaml +4 -5
  45. ultralytics/cfg/datasets/open-images-v7.yaml +7 -10
  46. ultralytics/cfg/datasets/package-seg.yaml +5 -5
  47. ultralytics/cfg/datasets/signature.yaml +4 -4
  48. ultralytics/cfg/datasets/tiger-pose.yaml +20 -4
  49. ultralytics/cfg/datasets/xView.yaml +5 -5
  50. ultralytics/cfg/default.yaml +96 -93
  51. ultralytics/cfg/trackers/botsort.yaml +16 -17
  52. ultralytics/cfg/trackers/bytetrack.yaml +9 -11
  53. ultralytics/data/__init__.py +4 -4
  54. ultralytics/data/annotator.py +12 -12
  55. ultralytics/data/augment.py +531 -564
  56. ultralytics/data/base.py +76 -81
  57. ultralytics/data/build.py +206 -42
  58. ultralytics/data/converter.py +179 -78
  59. ultralytics/data/dataset.py +121 -121
  60. ultralytics/data/loaders.py +114 -91
  61. ultralytics/data/split.py +28 -15
  62. ultralytics/data/split_dota.py +67 -48
  63. ultralytics/data/utils.py +110 -89
  64. ultralytics/engine/exporter.py +422 -460
  65. ultralytics/engine/model.py +224 -252
  66. ultralytics/engine/predictor.py +94 -89
  67. ultralytics/engine/results.py +345 -595
  68. ultralytics/engine/trainer.py +231 -134
  69. ultralytics/engine/tuner.py +279 -73
  70. ultralytics/engine/validator.py +53 -46
  71. ultralytics/hub/__init__.py +26 -28
  72. ultralytics/hub/auth.py +30 -16
  73. ultralytics/hub/google/__init__.py +34 -36
  74. ultralytics/hub/session.py +53 -77
  75. ultralytics/hub/utils.py +23 -109
  76. ultralytics/models/__init__.py +1 -1
  77. ultralytics/models/fastsam/__init__.py +1 -1
  78. ultralytics/models/fastsam/model.py +36 -18
  79. ultralytics/models/fastsam/predict.py +33 -44
  80. ultralytics/models/fastsam/utils.py +4 -5
  81. ultralytics/models/fastsam/val.py +12 -14
  82. ultralytics/models/nas/__init__.py +1 -1
  83. ultralytics/models/nas/model.py +16 -20
  84. ultralytics/models/nas/predict.py +12 -14
  85. ultralytics/models/nas/val.py +4 -5
  86. ultralytics/models/rtdetr/__init__.py +1 -1
  87. ultralytics/models/rtdetr/model.py +9 -9
  88. ultralytics/models/rtdetr/predict.py +22 -17
  89. ultralytics/models/rtdetr/train.py +20 -16
  90. ultralytics/models/rtdetr/val.py +79 -59
  91. ultralytics/models/sam/__init__.py +8 -2
  92. ultralytics/models/sam/amg.py +53 -38
  93. ultralytics/models/sam/build.py +29 -31
  94. ultralytics/models/sam/model.py +33 -38
  95. ultralytics/models/sam/modules/blocks.py +159 -182
  96. ultralytics/models/sam/modules/decoders.py +38 -47
  97. ultralytics/models/sam/modules/encoders.py +114 -133
  98. ultralytics/models/sam/modules/memory_attention.py +38 -31
  99. ultralytics/models/sam/modules/sam.py +114 -93
  100. ultralytics/models/sam/modules/tiny_encoder.py +268 -291
  101. ultralytics/models/sam/modules/transformer.py +59 -66
  102. ultralytics/models/sam/modules/utils.py +55 -72
  103. ultralytics/models/sam/predict.py +745 -341
  104. ultralytics/models/utils/loss.py +118 -107
  105. ultralytics/models/utils/ops.py +118 -71
  106. ultralytics/models/yolo/__init__.py +1 -1
  107. ultralytics/models/yolo/classify/predict.py +28 -26
  108. ultralytics/models/yolo/classify/train.py +50 -81
  109. ultralytics/models/yolo/classify/val.py +68 -61
  110. ultralytics/models/yolo/detect/predict.py +12 -15
  111. ultralytics/models/yolo/detect/train.py +56 -46
  112. ultralytics/models/yolo/detect/val.py +279 -223
  113. ultralytics/models/yolo/model.py +167 -86
  114. ultralytics/models/yolo/obb/predict.py +7 -11
  115. ultralytics/models/yolo/obb/train.py +23 -25
  116. ultralytics/models/yolo/obb/val.py +107 -99
  117. ultralytics/models/yolo/pose/__init__.py +1 -1
  118. ultralytics/models/yolo/pose/predict.py +12 -14
  119. ultralytics/models/yolo/pose/train.py +31 -69
  120. ultralytics/models/yolo/pose/val.py +119 -254
  121. ultralytics/models/yolo/segment/predict.py +21 -25
  122. ultralytics/models/yolo/segment/train.py +12 -66
  123. ultralytics/models/yolo/segment/val.py +126 -305
  124. ultralytics/models/yolo/world/train.py +53 -45
  125. ultralytics/models/yolo/world/train_world.py +51 -32
  126. ultralytics/models/yolo/yoloe/__init__.py +7 -7
  127. ultralytics/models/yolo/yoloe/predict.py +30 -37
  128. ultralytics/models/yolo/yoloe/train.py +89 -71
  129. ultralytics/models/yolo/yoloe/train_seg.py +15 -17
  130. ultralytics/models/yolo/yoloe/val.py +56 -41
  131. ultralytics/nn/__init__.py +9 -11
  132. ultralytics/nn/autobackend.py +179 -107
  133. ultralytics/nn/modules/__init__.py +67 -67
  134. ultralytics/nn/modules/activation.py +8 -7
  135. ultralytics/nn/modules/block.py +302 -323
  136. ultralytics/nn/modules/conv.py +61 -104
  137. ultralytics/nn/modules/head.py +488 -186
  138. ultralytics/nn/modules/transformer.py +183 -123
  139. ultralytics/nn/modules/utils.py +15 -20
  140. ultralytics/nn/tasks.py +327 -203
  141. ultralytics/nn/text_model.py +81 -65
  142. ultralytics/py.typed +1 -0
  143. ultralytics/solutions/__init__.py +12 -12
  144. ultralytics/solutions/ai_gym.py +19 -27
  145. ultralytics/solutions/analytics.py +36 -26
  146. ultralytics/solutions/config.py +29 -28
  147. ultralytics/solutions/distance_calculation.py +23 -24
  148. ultralytics/solutions/heatmap.py +17 -19
  149. ultralytics/solutions/instance_segmentation.py +21 -19
  150. ultralytics/solutions/object_blurrer.py +16 -17
  151. ultralytics/solutions/object_counter.py +48 -53
  152. ultralytics/solutions/object_cropper.py +22 -16
  153. ultralytics/solutions/parking_management.py +61 -58
  154. ultralytics/solutions/queue_management.py +19 -19
  155. ultralytics/solutions/region_counter.py +63 -50
  156. ultralytics/solutions/security_alarm.py +22 -25
  157. ultralytics/solutions/similarity_search.py +107 -60
  158. ultralytics/solutions/solutions.py +343 -262
  159. ultralytics/solutions/speed_estimation.py +35 -31
  160. ultralytics/solutions/streamlit_inference.py +104 -40
  161. ultralytics/solutions/templates/similarity-search.html +31 -24
  162. ultralytics/solutions/trackzone.py +24 -24
  163. ultralytics/solutions/vision_eye.py +11 -12
  164. ultralytics/trackers/__init__.py +1 -1
  165. ultralytics/trackers/basetrack.py +18 -27
  166. ultralytics/trackers/bot_sort.py +48 -39
  167. ultralytics/trackers/byte_tracker.py +94 -94
  168. ultralytics/trackers/track.py +7 -16
  169. ultralytics/trackers/utils/gmc.py +37 -69
  170. ultralytics/trackers/utils/kalman_filter.py +68 -76
  171. ultralytics/trackers/utils/matching.py +13 -17
  172. ultralytics/utils/__init__.py +251 -275
  173. ultralytics/utils/autobatch.py +19 -7
  174. ultralytics/utils/autodevice.py +68 -38
  175. ultralytics/utils/benchmarks.py +169 -130
  176. ultralytics/utils/callbacks/base.py +12 -13
  177. ultralytics/utils/callbacks/clearml.py +14 -15
  178. ultralytics/utils/callbacks/comet.py +139 -66
  179. ultralytics/utils/callbacks/dvc.py +19 -27
  180. ultralytics/utils/callbacks/hub.py +8 -6
  181. ultralytics/utils/callbacks/mlflow.py +6 -10
  182. ultralytics/utils/callbacks/neptune.py +11 -19
  183. ultralytics/utils/callbacks/platform.py +73 -0
  184. ultralytics/utils/callbacks/raytune.py +3 -4
  185. ultralytics/utils/callbacks/tensorboard.py +9 -12
  186. ultralytics/utils/callbacks/wb.py +33 -30
  187. ultralytics/utils/checks.py +163 -114
  188. ultralytics/utils/cpu.py +89 -0
  189. ultralytics/utils/dist.py +24 -20
  190. ultralytics/utils/downloads.py +176 -146
  191. ultralytics/utils/errors.py +11 -13
  192. ultralytics/utils/events.py +113 -0
  193. ultralytics/utils/export/__init__.py +7 -0
  194. ultralytics/utils/{export.py → export/engine.py} +81 -63
  195. ultralytics/utils/export/imx.py +294 -0
  196. ultralytics/utils/export/tensorflow.py +217 -0
  197. ultralytics/utils/files.py +33 -36
  198. ultralytics/utils/git.py +137 -0
  199. ultralytics/utils/instance.py +105 -120
  200. ultralytics/utils/logger.py +404 -0
  201. ultralytics/utils/loss.py +99 -61
  202. ultralytics/utils/metrics.py +649 -478
  203. ultralytics/utils/nms.py +337 -0
  204. ultralytics/utils/ops.py +263 -451
  205. ultralytics/utils/patches.py +70 -31
  206. ultralytics/utils/plotting.py +253 -223
  207. ultralytics/utils/tal.py +48 -61
  208. ultralytics/utils/torch_utils.py +244 -251
  209. ultralytics/utils/tqdm.py +438 -0
  210. ultralytics/utils/triton.py +22 -23
  211. ultralytics/utils/tuner.py +11 -10
  212. dgenerate_ultralytics_headless-8.3.137.dist-info/RECORD +0 -272
  213. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/entry_points.txt +0 -0
  214. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/licenses/LICENSE +0 -0
  215. {dgenerate_ultralytics_headless-8.3.137.dist-info → dgenerate_ultralytics_headless-8.3.224.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,7 @@
1
1
  # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import contextlib
4
6
  import importlib.metadata
5
7
  import inspect
@@ -8,24 +10,24 @@ import logging
8
10
  import os
9
11
  import platform
10
12
  import re
11
- import subprocess
13
+ import socket
12
14
  import sys
13
15
  import threading
14
16
  import time
15
- import warnings
17
+ from functools import lru_cache
16
18
  from pathlib import Path
17
19
  from threading import Lock
18
20
  from types import SimpleNamespace
19
- from typing import Union
20
21
  from urllib.parse import unquote
21
22
 
22
23
  import cv2
23
24
  import numpy as np
24
25
  import torch
25
- import tqdm
26
26
 
27
27
  from ultralytics import __version__
28
- from ultralytics.utils.patches import imread, imshow, imwrite, torch_load, torch_save # for patches
28
+ from ultralytics.utils.git import GitRepo
29
+ from ultralytics.utils.patches import imread, imshow, imwrite, torch_save # for patches
30
+ from ultralytics.utils.tqdm import TQDM # noqa
29
31
 
30
32
  # PyTorch Multi-GPU DDP Constants
31
33
  RANK = int(os.getenv("RANK", -1))
@@ -41,13 +43,13 @@ DEFAULT_CFG_PATH = ROOT / "cfg/default.yaml"
41
43
  NUM_THREADS = min(8, max(1, os.cpu_count() - 1)) # number of YOLO multiprocessing threads
42
44
  AUTOINSTALL = str(os.getenv("YOLO_AUTOINSTALL", True)).lower() == "true" # global auto-install mode
43
45
  VERBOSE = str(os.getenv("YOLO_VERBOSE", True)).lower() == "true" # global verbose mode
44
- TQDM_BAR_FORMAT = "{l_bar}{bar:10}{r_bar}" if VERBOSE else None # tqdm bar format
45
46
  LOGGING_NAME = "ultralytics"
46
47
  MACOS, LINUX, WINDOWS = (platform.system() == x for x in ["Darwin", "Linux", "Windows"]) # environment booleans
47
48
  MACOS_VERSION = platform.mac_ver()[0] if MACOS else None
49
+ NOT_MACOS14 = not (MACOS and MACOS_VERSION.startswith("14."))
48
50
  ARM64 = platform.machine() in {"arm64", "aarch64"} # ARM64 booleans
49
51
  PYTHON_VERSION = platform.python_version()
50
- TORCH_VERSION = torch.__version__
52
+ TORCH_VERSION = str(torch.__version__) # Normalize torch.__version__ (PyTorch>1.9 returns TorchVersion objects)
51
53
  TORCHVISION_VERSION = importlib.metadata.version("torchvision") # faster than importing torchvision
52
54
  IS_VSCODE = os.environ.get("TERM_PROGRAM", False) == "vscode"
53
55
  RKNN_CHIPS = frozenset(
@@ -130,74 +132,100 @@ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # suppress verbose TF compiler warning
130
132
  os.environ["TORCH_CPP_LOG_LEVEL"] = "ERROR" # suppress "NNPACK.cpp could not initialize NNPACK" warnings
131
133
  os.environ["KINETO_LOG_LEVEL"] = "5" # suppress verbose PyTorch profiler output when computing FLOPs
132
134
 
133
- if TQDM_RICH := str(os.getenv("YOLO_TQDM_RICH", False)).lower() == "true":
134
- from tqdm import rich
135
-
135
+ # Precompiled type tuples for faster isinstance() checks
136
+ FLOAT_OR_INT = (float, int)
137
+ STR_OR_PATH = (str, Path)
136
138
 
137
- class TQDM(rich.tqdm if TQDM_RICH else tqdm.tqdm):
138
- """
139
- A custom TQDM progress bar class that extends the original tqdm functionality.
140
139
 
141
- This class modifies the behavior of the original tqdm progress bar based on global settings and provides
142
- additional customization options.
140
+ class DataExportMixin:
141
+ """Mixin class for exporting validation metrics or prediction results in various formats.
143
142
 
144
- Attributes:
145
- disable (bool): Whether to disable the progress bar. Determined by the global VERBOSE setting and
146
- any passed 'disable' argument.
147
- bar_format (str): The format string for the progress bar. Uses the global TQDM_BAR_FORMAT if not
148
- explicitly set.
143
+ This class provides utilities to export performance metrics (e.g., mAP, precision, recall) or prediction results
144
+ from classification, object detection, segmentation, or pose estimation tasks into various formats: Polars
145
+ DataFrame, CSV and JSON.
149
146
 
150
147
  Methods:
151
- __init__: Initializes the TQDM object with custom settings.
148
+ to_df: Convert summary to a Polars DataFrame.
149
+ to_csv: Export results as a CSV string.
150
+ to_json: Export results as a JSON string.
151
+ tojson: Deprecated alias for `to_json()`.
152
152
 
153
153
  Examples:
154
- >>> from ultralytics.utils import TQDM
155
- >>> for i in TQDM(range(100)):
156
- ... # Your processing code here
157
- ... pass
154
+ >>> model = YOLO("yolo11n.pt")
155
+ >>> results = model("image.jpg")
156
+ >>> df = results.to_df()
157
+ >>> print(df)
158
+ >>> csv_data = results.to_csv()
158
159
  """
159
160
 
160
- def __init__(self, *args, **kwargs):
161
+ def to_df(self, normalize=False, decimals=5):
162
+ """Create a polars DataFrame from the prediction results summary or validation metrics.
163
+
164
+ Args:
165
+ normalize (bool, optional): Normalize numerical values for easier comparison.
166
+ decimals (int, optional): Decimal places to round floats.
167
+
168
+ Returns:
169
+ (DataFrame): DataFrame containing the summary data.
161
170
  """
162
- Initializes a custom TQDM progress bar.
171
+ import polars as pl # scope for faster 'import ultralytics'
172
+
173
+ return pl.DataFrame(self.summary(normalize=normalize, decimals=decimals))
163
174
 
164
- This class extends the original tqdm class to provide customized behavior for Ultralytics projects.
175
+ def to_csv(self, normalize=False, decimals=5):
176
+ """Export results or metrics to CSV string format.
165
177
 
166
178
  Args:
167
- *args (Any): Variable length argument list to be passed to the original tqdm constructor.
168
- **kwargs (Any): Arbitrary keyword arguments to be passed to the original tqdm constructor.
169
-
170
- Notes:
171
- - The progress bar is disabled if VERBOSE is False or if 'disable' is explicitly set to True in kwargs.
172
- - The default bar format is set to TQDM_BAR_FORMAT unless overridden in kwargs.
173
-
174
- Examples:
175
- >>> from ultralytics.utils import TQDM
176
- >>> for i in TQDM(range(100)):
177
- ... # Your code here
178
- ... pass
179
+ normalize (bool, optional): Normalize numeric values.
180
+ decimals (int, optional): Decimal precision.
181
+
182
+ Returns:
183
+ (str): CSV content as string.
179
184
  """
180
- warnings.filterwarnings("ignore", category=tqdm.TqdmExperimentalWarning) # suppress tqdm.rich warning
181
- kwargs["disable"] = not VERBOSE or kwargs.get("disable", False)
182
- kwargs.setdefault("bar_format", TQDM_BAR_FORMAT) # override default value if passed
183
- super().__init__(*args, **kwargs)
185
+ import polars as pl
184
186
 
185
- def __iter__(self):
186
- """Return self as iterator to satisfy Iterable interface."""
187
- return super().__iter__()
187
+ df = self.to_df(normalize=normalize, decimals=decimals)
188
+
189
+ try:
190
+ return df.write_csv()
191
+ except Exception:
192
+ # Minimal string conversion for any remaining complex types
193
+ def _to_str_simple(v):
194
+ if v is None:
195
+ return ""
196
+ elif isinstance(v, (dict, list, tuple, set)):
197
+ return repr(v)
198
+ else:
199
+ return str(v)
200
+
201
+ df_str = df.select(
202
+ [pl.col(c).map_elements(_to_str_simple, return_dtype=pl.String).alias(c) for c in df.columns]
203
+ )
204
+ return df_str.write_csv()
205
+
206
+ def to_json(self, normalize=False, decimals=5):
207
+ """Export results to JSON format.
208
+
209
+ Args:
210
+ normalize (bool, optional): Normalize numeric values.
211
+ decimals (int, optional): Decimal precision.
212
+
213
+ Returns:
214
+ (str): JSON-formatted string of the results.
215
+ """
216
+ return self.to_df(normalize=normalize, decimals=decimals).write_json()
188
217
 
189
218
 
190
219
  class SimpleClass:
191
- """
192
- A simple base class for creating objects with string representations of their attributes.
220
+ """A simple base class for creating objects with string representations of their attributes.
193
221
 
194
- This class provides a foundation for creating objects that can be easily printed or represented as strings,
195
- showing all their non-callable attributes. It's useful for debugging and introspection of object states.
222
+ This class provides a foundation for creating objects that can be easily printed or represented as strings, showing
223
+ all their non-callable attributes. It's useful for debugging and introspection of object states.
196
224
 
197
225
  Methods:
198
- __str__: Returns a human-readable string representation of the object.
199
- __repr__: Returns a machine-readable string representation of the object.
200
- __getattr__: Provides a custom attribute access error message with helpful information.
226
+ __str__: Return a human-readable string representation of the object.
227
+ __repr__: Return a machine-readable string representation of the object.
228
+ __getattr__: Provide a custom attribute access error message with helpful information.
201
229
 
202
230
  Examples:
203
231
  >>> class MyClass(SimpleClass):
@@ -227,7 +255,7 @@ class SimpleClass:
227
255
  # Display only the module and class name for subclasses
228
256
  s = f"{a}: {v.__module__}.{v.__class__.__name__} object"
229
257
  else:
230
- s = f"{a}: {repr(v)}"
258
+ s = f"{a}: {v!r}"
231
259
  attr.append(s)
232
260
  return f"{self.__module__}.{self.__class__.__name__} object with attributes:\n\n" + "\n".join(attr)
233
261
 
@@ -236,24 +264,23 @@ class SimpleClass:
236
264
  return self.__str__()
237
265
 
238
266
  def __getattr__(self, attr):
239
- """Custom attribute access error message with helpful information."""
267
+ """Provide a custom attribute access error message with helpful information."""
240
268
  name = self.__class__.__name__
241
269
  raise AttributeError(f"'{name}' object has no attribute '{attr}'. See valid attributes below.\n{self.__doc__}")
242
270
 
243
271
 
244
272
  class IterableSimpleNamespace(SimpleNamespace):
245
- """
246
- An iterable SimpleNamespace class that provides enhanced functionality for attribute access and iteration.
273
+ """An iterable SimpleNamespace class that provides enhanced functionality for attribute access and iteration.
247
274
 
248
- This class extends the SimpleNamespace class with additional methods for iteration, string representation,
249
- and attribute access. It is designed to be used as a convenient container for storing and accessing
250
- configuration parameters.
275
+ This class extends the SimpleNamespace class with additional methods for iteration, string representation, and
276
+ attribute access. It is designed to be used as a convenient container for storing and accessing configuration
277
+ parameters.
251
278
 
252
279
  Methods:
253
- __iter__: Returns an iterator of key-value pairs from the namespace's attributes.
254
- __str__: Returns a human-readable string representation of the object.
255
- __getattr__: Provides a custom attribute access error message with helpful information.
256
- get: Retrieves the value of a specified key, or a default value if the key doesn't exist.
280
+ __iter__: Return an iterator of key-value pairs from the namespace's attributes.
281
+ __str__: Return a human-readable string representation of the object.
282
+ __getattr__: Provide a custom attribute access error message with helpful information.
283
+ get: Retrieve the value of a specified key, or a default value if the key doesn't exist.
257
284
 
258
285
  Examples:
259
286
  >>> cfg = IterableSimpleNamespace(a=1, b=2, c=3)
@@ -285,7 +312,7 @@ class IterableSimpleNamespace(SimpleNamespace):
285
312
  return "\n".join(f"{k}={v}" for k, v in vars(self).items())
286
313
 
287
314
  def __getattr__(self, attr):
288
- """Custom attribute access error message with helpful information."""
315
+ """Provide a custom attribute access error message with helpful information."""
289
316
  name = self.__class__.__name__
290
317
  raise AttributeError(
291
318
  f"""
@@ -302,12 +329,11 @@ class IterableSimpleNamespace(SimpleNamespace):
302
329
 
303
330
 
304
331
  def plt_settings(rcparams=None, backend="Agg"):
305
- """
306
- Decorator to temporarily set rc parameters and the backend for a plotting function.
332
+ """Decorator to temporarily set rc parameters and the backend for a plotting function.
307
333
 
308
334
  Args:
309
335
  rcparams (dict, optional): Dictionary of rc parameters to set.
310
- backend (str, optional): Name of the backend to use. Defaults to 'Agg'.
336
+ backend (str, optional): Name of the backend to use.
311
337
 
312
338
  Returns:
313
339
  (Callable): Decorated function with temporarily set rc parameters and backend.
@@ -331,7 +357,7 @@ def plt_settings(rcparams=None, backend="Agg"):
331
357
  """Decorator to apply temporary rc parameters and backend to a function."""
332
358
 
333
359
  def wrapper(*args, **kwargs):
334
- """Sets rc parameters and backend, calls the original function, and restores the settings."""
360
+ """Set rc parameters and backend, call the original function, and restore the settings."""
335
361
  import matplotlib.pyplot as plt # scope for faster 'import ultralytics'
336
362
 
337
363
  original_backend = plt.get_backend()
@@ -356,16 +382,15 @@ def plt_settings(rcparams=None, backend="Agg"):
356
382
 
357
383
 
358
384
  def set_logging(name="LOGGING_NAME", verbose=True):
359
- """
360
- Sets up logging with UTF-8 encoding and configurable verbosity.
385
+ """Set up logging with UTF-8 encoding and configurable verbosity.
361
386
 
362
- This function configures logging for the Ultralytics library, setting the appropriate logging level and
363
- formatter based on the verbosity flag and the current process rank. It handles special cases for Windows
364
- environments where UTF-8 encoding might not be the default.
387
+ This function configures logging for the Ultralytics library, setting the appropriate logging level and formatter
388
+ based on the verbosity flag and the current process rank. It handles special cases for Windows environments where
389
+ UTF-8 encoding might not be the default.
365
390
 
366
391
  Args:
367
- name (str): Name of the logger. Defaults to "LOGGING_NAME".
368
- verbose (bool): Flag to set logging level to INFO if True, ERROR otherwise. Defaults to True.
392
+ name (str): Name of the logger.
393
+ verbose (bool): Flag to set logging level to INFO if True, ERROR otherwise.
369
394
 
370
395
  Returns:
371
396
  (logging.Logger): Configured logger object.
@@ -388,10 +413,10 @@ def set_logging(name="LOGGING_NAME", verbose=True):
388
413
  """Format log records with prefixes based on level."""
389
414
  # Apply prefixes based on log level
390
415
  if record.levelno == logging.WARNING:
391
- prefix = "WARNING ⚠️" if not WINDOWS else "WARNING"
416
+ prefix = "WARNING" if WINDOWS else "WARNING ⚠️"
392
417
  record.msg = f"{prefix} {record.msg}"
393
418
  elif record.levelno == logging.ERROR:
394
- prefix = "ERROR" if not WINDOWS else "ERROR"
419
+ prefix = "ERROR" if WINDOWS else "ERROR"
395
420
  record.msg = f"{prefix} {record.msg}"
396
421
 
397
422
  # Handle emojis in message based on platform
@@ -402,7 +427,7 @@ def set_logging(name="LOGGING_NAME", verbose=True):
402
427
 
403
428
  # Handle Windows UTF-8 encoding issues
404
429
  if WINDOWS and hasattr(sys.stdout, "encoding") and sys.stdout.encoding != "utf-8":
405
- try:
430
+ with contextlib.suppress(Exception):
406
431
  # Attempt to reconfigure stdout to use UTF-8 encoding if possible
407
432
  if hasattr(sys.stdout, "reconfigure"):
408
433
  sys.stdout.reconfigure(encoding="utf-8")
@@ -411,8 +436,6 @@ def set_logging(name="LOGGING_NAME", verbose=True):
411
436
  import io
412
437
 
413
438
  sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
414
- except Exception:
415
- pass
416
439
 
417
440
  # Create and configure the StreamHandler with the appropriate formatter and level
418
441
  stream_handler = logging.StreamHandler(sys.stdout)
@@ -429,8 +452,7 @@ def set_logging(name="LOGGING_NAME", verbose=True):
429
452
 
430
453
  # Set logger
431
454
  LOGGER = set_logging(LOGGING_NAME, verbose=VERBOSE) # define globally (used in train.py, val.py, predict.py, etc.)
432
- for logger in "sentry_sdk", "urllib3.connectionpool":
433
- logging.getLogger(logger).setLevel(logging.CRITICAL + 1)
455
+ logging.getLogger("sentry_sdk").setLevel(logging.CRITICAL + 1)
434
456
 
435
457
 
436
458
  def emojis(string=""):
@@ -439,8 +461,7 @@ def emojis(string=""):
439
461
 
440
462
 
441
463
  class ThreadingLocked:
442
- """
443
- A decorator class for ensuring thread-safe execution of a function or method.
464
+ """A decorator class for ensuring thread-safe execution of a function or method.
444
465
 
445
466
  This class can be used as a decorator to make sure that if the decorated function is called from multiple threads,
446
467
  only one thread at a time will be able to execute the function.
@@ -465,7 +486,7 @@ class ThreadingLocked:
465
486
 
466
487
  @wraps(f)
467
488
  def decorated(*args, **kwargs):
468
- """Applies thread-safety to the decorated function or method."""
489
+ """Apply thread-safety to the decorated function or method."""
469
490
  with self.lock:
470
491
  return f(*args, **kwargs)
471
492
 
@@ -473,8 +494,7 @@ class ThreadingLocked:
473
494
 
474
495
 
475
496
  class YAML:
476
- """
477
- YAML utility class for efficient file operations with automatic C-implementation detection.
497
+ """YAML utility class for efficient file operations with automatic C-implementation detection.
478
498
 
479
499
  This class provides optimized YAML loading and saving operations using PyYAML's fastest available implementation
480
500
  (C-based when possible). It implements a singleton pattern with lazy initialization, allowing direct class method
@@ -524,8 +544,7 @@ class YAML:
524
544
 
525
545
  @classmethod
526
546
  def save(cls, file="data.yaml", data=None, header=""):
527
- """
528
- Save Python object as YAML file.
547
+ """Save Python object as YAML file.
529
548
 
530
549
  Args:
531
550
  file (str | Path): Path to save YAML file.
@@ -554,8 +573,7 @@ class YAML:
554
573
 
555
574
  @classmethod
556
575
  def load(cls, file="data.yaml", append_filename=False):
557
- """
558
- Load YAML file to Python object with robust error handling.
576
+ """Load YAML file to Python object with robust error handling.
559
577
 
560
578
  Args:
561
579
  file (str | Path): Path to YAML file.
@@ -589,8 +607,7 @@ class YAML:
589
607
 
590
608
  @classmethod
591
609
  def print(cls, yaml_file):
592
- """
593
- Pretty print YAML file or object to console.
610
+ """Pretty print YAML file or object to console.
594
611
 
595
612
  Args:
596
613
  yaml_file (str | Path | dict): Path to YAML file or dict to print.
@@ -613,8 +630,7 @@ DEFAULT_CFG = IterableSimpleNamespace(**DEFAULT_CFG_DICT)
613
630
 
614
631
 
615
632
  def read_device_model() -> str:
616
- """
617
- Reads the device model information from the system and caches it for quick access.
633
+ """Read the device model information from the system and cache it for quick access.
618
634
 
619
635
  Returns:
620
636
  (str): Kernel release information.
@@ -623,8 +639,7 @@ def read_device_model() -> str:
623
639
 
624
640
 
625
641
  def is_ubuntu() -> bool:
626
- """
627
- Check if the OS is Ubuntu.
642
+ """Check if the OS is Ubuntu.
628
643
 
629
644
  Returns:
630
645
  (bool): True if OS is Ubuntu, False otherwise.
@@ -637,8 +652,7 @@ def is_ubuntu() -> bool:
637
652
 
638
653
 
639
654
  def is_colab():
640
- """
641
- Check if the current script is running inside a Google Colab notebook.
655
+ """Check if the current script is running inside a Google Colab notebook.
642
656
 
643
657
  Returns:
644
658
  (bool): True if running inside a Colab notebook, False otherwise.
@@ -647,8 +661,7 @@ def is_colab():
647
661
 
648
662
 
649
663
  def is_kaggle():
650
- """
651
- Check if the current script is running inside a Kaggle kernel.
664
+ """Check if the current script is running inside a Kaggle kernel.
652
665
 
653
666
  Returns:
654
667
  (bool): True if running inside a Kaggle kernel, False otherwise.
@@ -657,13 +670,12 @@ def is_kaggle():
657
670
 
658
671
 
659
672
  def is_jupyter():
660
- """
661
- Check if the current script is running inside a Jupyter Notebook.
673
+ """Check if the current script is running inside a Jupyter Notebook.
662
674
 
663
675
  Returns:
664
676
  (bool): True if running inside a Jupyter Notebook, False otherwise.
665
677
 
666
- Note:
678
+ Notes:
667
679
  - Only works on Colab and Kaggle, other environments like Jupyterlab and Paperspace are not reliably detectable.
668
680
  - "get_ipython" in globals() method suffers false positives when IPython package installed manually.
669
681
  """
@@ -671,8 +683,7 @@ def is_jupyter():
671
683
 
672
684
 
673
685
  def is_runpod():
674
- """
675
- Check if the current script is running inside a RunPod container.
686
+ """Check if the current script is running inside a RunPod container.
676
687
 
677
688
  Returns:
678
689
  (bool): True if running in RunPod, False otherwise.
@@ -681,22 +692,19 @@ def is_runpod():
681
692
 
682
693
 
683
694
  def is_docker() -> bool:
684
- """
685
- Determine if the script is running inside a Docker container.
695
+ """Determine if the script is running inside a Docker container.
686
696
 
687
697
  Returns:
688
698
  (bool): True if the script is running inside a Docker container, False otherwise.
689
699
  """
690
700
  try:
691
- with open("/proc/self/cgroup") as f:
692
- return "docker" in f.read()
701
+ return os.path.exists("/.dockerenv")
693
702
  except Exception:
694
703
  return False
695
704
 
696
705
 
697
706
  def is_raspberrypi() -> bool:
698
- """
699
- Determines if the Python environment is running on a Raspberry Pi.
707
+ """Determine if the Python environment is running on a Raspberry Pi.
700
708
 
701
709
  Returns:
702
710
  (bool): True if running on a Raspberry Pi, False otherwise.
@@ -704,37 +712,47 @@ def is_raspberrypi() -> bool:
704
712
  return "rpi" in DEVICE_MODEL
705
713
 
706
714
 
707
- def is_jetson() -> bool:
708
- """
709
- Determines if the Python environment is running on an NVIDIA Jetson device.
715
+ @lru_cache(maxsize=3)
716
+ def is_jetson(jetpack=None) -> bool:
717
+ """Determine if the Python environment is running on an NVIDIA Jetson device.
718
+
719
+ Args:
720
+ jetpack (int | None): If specified, check for specific JetPack version (4, 5, 6).
710
721
 
711
722
  Returns:
712
723
  (bool): True if running on an NVIDIA Jetson device, False otherwise.
713
724
  """
714
- return "tegra" in DEVICE_MODEL
725
+ if jetson := ("tegra" in DEVICE_MODEL):
726
+ if jetpack:
727
+ try:
728
+ content = open("/etc/nv_tegra_release").read()
729
+ version_map = {4: "R32", 5: "R35", 6: "R36"} # JetPack to L4T major version mapping
730
+ return jetpack in version_map and version_map[jetpack] in content
731
+ except Exception:
732
+ return False
733
+ return jetson
715
734
 
716
735
 
717
736
  def is_online() -> bool:
718
- """
719
- Check internet connectivity by attempting to connect to a known online host.
737
+ """Fast online check using DNS (v4/v6) resolution (Cloudflare + Google).
720
738
 
721
739
  Returns:
722
740
  (bool): True if connection is successful, False otherwise.
723
741
  """
724
- try:
725
- assert str(os.getenv("YOLO_OFFLINE", "")).lower() != "true" # check if ENV var YOLO_OFFLINE="True"
726
- import socket
742
+ if str(os.getenv("YOLO_OFFLINE", "")).lower() == "true":
743
+ return False
727
744
 
728
- for dns in ("1.1.1.1", "8.8.8.8"): # check Cloudflare and Google DNS
729
- socket.create_connection(address=(dns, 80), timeout=2.0).close()
745
+ for host in ("one.one.one.one", "dns.google"):
746
+ try:
747
+ socket.getaddrinfo(host, 0, socket.AF_UNSPEC, 0, 0, socket.AI_ADDRCONFIG)
730
748
  return True
731
- except Exception:
732
- return False
749
+ except OSError:
750
+ continue
751
+ return False
733
752
 
734
753
 
735
754
  def is_pip_package(filepath: str = __name__) -> bool:
736
- """
737
- Determines if the file at the given filepath is part of a pip package.
755
+ """Determine if the file at the given filepath is part of a pip package.
738
756
 
739
757
  Args:
740
758
  filepath (str): The filepath to check.
@@ -751,22 +769,20 @@ def is_pip_package(filepath: str = __name__) -> bool:
751
769
  return spec is not None and spec.origin is not None
752
770
 
753
771
 
754
- def is_dir_writeable(dir_path: Union[str, Path]) -> bool:
755
- """
756
- Check if a directory is writeable.
772
+ def is_dir_writeable(dir_path: str | Path) -> bool:
773
+ """Check if a directory is writable.
757
774
 
758
775
  Args:
759
776
  dir_path (str | Path): The path to the directory.
760
777
 
761
778
  Returns:
762
- (bool): True if the directory is writeable, False otherwise.
779
+ (bool): True if the directory is writable, False otherwise.
763
780
  """
764
781
  return os.access(str(dir_path), os.W_OK)
765
782
 
766
783
 
767
784
  def is_pytest_running():
768
- """
769
- Determines whether pytest is currently running or not.
785
+ """Determine whether pytest is currently running or not.
770
786
 
771
787
  Returns:
772
788
  (bool): True if pytest is running, False otherwise.
@@ -775,8 +791,7 @@ def is_pytest_running():
775
791
 
776
792
 
777
793
  def is_github_action_running() -> bool:
778
- """
779
- Determine if the current environment is a GitHub Actions runner.
794
+ """Determine if the current environment is a GitHub Actions runner.
780
795
 
781
796
  Returns:
782
797
  (bool): True if the current environment is a GitHub Actions runner, False otherwise.
@@ -784,61 +799,8 @@ def is_github_action_running() -> bool:
784
799
  return "GITHUB_ACTIONS" in os.environ and "GITHUB_WORKFLOW" in os.environ and "RUNNER_OS" in os.environ
785
800
 
786
801
 
787
- def get_git_dir():
788
- """
789
- Determines whether the current file is part of a git repository and if so, returns the repository root directory.
790
-
791
- Returns:
792
- (Path | None): Git root directory if found or None if not found.
793
- """
794
- for d in Path(__file__).parents:
795
- if (d / ".git").is_dir():
796
- return d
797
-
798
-
799
- def is_git_dir():
800
- """
801
- Determines whether the current file is part of a git repository.
802
-
803
- Returns:
804
- (bool): True if current file is part of a git repository.
805
- """
806
- return GIT_DIR is not None
807
-
808
-
809
- def get_git_origin_url():
810
- """
811
- Retrieves the origin URL of a git repository.
812
-
813
- Returns:
814
- (str | None): The origin URL of the git repository or None if not git directory.
815
- """
816
- if IS_GIT_DIR:
817
- try:
818
- origin = subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
819
- return origin.decode().strip()
820
- except subprocess.CalledProcessError:
821
- return None
822
-
823
-
824
- def get_git_branch():
825
- """
826
- Returns the current git branch name. If not in a git repository, returns None.
827
-
828
- Returns:
829
- (str | None): The current git branch name or None if not a git directory.
830
- """
831
- if IS_GIT_DIR:
832
- try:
833
- origin = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
834
- return origin.decode().strip()
835
- except subprocess.CalledProcessError:
836
- return None
837
-
838
-
839
802
  def get_default_args(func):
840
- """
841
- Returns a dictionary of default arguments for a function.
803
+ """Return a dictionary of default arguments for a function.
842
804
 
843
805
  Args:
844
806
  func (callable): The function to inspect.
@@ -851,8 +813,7 @@ def get_default_args(func):
851
813
 
852
814
 
853
815
  def get_ubuntu_version():
854
- """
855
- Retrieve the Ubuntu version if the OS is Ubuntu.
816
+ """Retrieve the Ubuntu version if the OS is Ubuntu.
856
817
 
857
818
  Returns:
858
819
  (str): Ubuntu version or None if not an Ubuntu OS.
@@ -866,8 +827,7 @@ def get_ubuntu_version():
866
827
 
867
828
 
868
829
  def get_user_config_dir(sub_dir="Ultralytics"):
869
- """
870
- Return the appropriate config directory based on the environment operating system.
830
+ """Return a writable config dir, preferring YOLO_CONFIG_DIR and being OS-aware.
871
831
 
872
832
  Args:
873
833
  sub_dir (str): The name of the subdirectory to create.
@@ -875,27 +835,38 @@ def get_user_config_dir(sub_dir="Ultralytics"):
875
835
  Returns:
876
836
  (Path): The path to the user config directory.
877
837
  """
878
- if WINDOWS:
879
- path = Path.home() / "AppData" / "Roaming" / sub_dir
880
- elif MACOS: # macOS
881
- path = Path.home() / "Library" / "Application Support" / sub_dir
838
+ if env_dir := os.getenv("YOLO_CONFIG_DIR"):
839
+ p = Path(env_dir).expanduser() / sub_dir
882
840
  elif LINUX:
883
- path = Path.home() / ".config" / sub_dir
841
+ p = Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config")) / sub_dir
842
+ elif WINDOWS:
843
+ p = Path.home() / "AppData" / "Roaming" / sub_dir
844
+ elif MACOS:
845
+ p = Path.home() / "Library" / "Application Support" / sub_dir
884
846
  else:
885
847
  raise ValueError(f"Unsupported operating system: {platform.system()}")
886
848
 
887
- # GCP and AWS lambda fix, only /tmp is writeable
888
- if not is_dir_writeable(path.parent):
889
- LOGGER.warning(
890
- f"user config directory '{path}' is not writeable, defaulting to '/tmp' or CWD."
891
- "Alternatively you can define a YOLO_CONFIG_DIR environment variable for this path."
892
- )
893
- path = Path("/tmp") / sub_dir if is_dir_writeable("/tmp") else Path().cwd() / sub_dir
894
-
895
- # Create the subdirectory if it does not exist
896
- path.mkdir(parents=True, exist_ok=True)
849
+ if p.exists(): # already created trust it
850
+ return p
851
+ if is_dir_writeable(p.parent): # create if possible
852
+ p.mkdir(parents=True, exist_ok=True)
853
+ return p
854
+
855
+ # Fallbacks for Docker, GCP/AWS functions where only /tmp is writable
856
+ for alt in [Path("/tmp") / sub_dir, Path.cwd() / sub_dir]:
857
+ if alt.exists():
858
+ return alt
859
+ if is_dir_writeable(alt.parent):
860
+ alt.mkdir(parents=True, exist_ok=True)
861
+ LOGGER.warning(
862
+ f"user config directory '{p}' is not writable, using '{alt}'. Set YOLO_CONFIG_DIR to override."
863
+ )
864
+ return alt
897
865
 
898
- return path
866
+ # Last fallback → CWD
867
+ p = Path.cwd() / sub_dir
868
+ p.mkdir(parents=True, exist_ok=True)
869
+ return p
899
870
 
900
871
 
901
872
  # Define constants (required below)
@@ -908,16 +879,13 @@ IS_JETSON = is_jetson()
908
879
  IS_JUPYTER = is_jupyter()
909
880
  IS_PIP_PACKAGE = is_pip_package()
910
881
  IS_RASPBERRYPI = is_raspberrypi()
911
- GIT_DIR = get_git_dir()
912
- IS_GIT_DIR = is_git_dir()
913
- USER_CONFIG_DIR = Path(os.getenv("YOLO_CONFIG_DIR") or get_user_config_dir()) # Ultralytics settings dir
882
+ GIT = GitRepo()
883
+ USER_CONFIG_DIR = get_user_config_dir() # Ultralytics settings dir
914
884
  SETTINGS_FILE = USER_CONFIG_DIR / "settings.json"
915
885
 
916
886
 
917
887
  def colorstr(*input):
918
- r"""
919
- Colors a string based on the provided color and style arguments. Utilizes ANSI escape codes.
920
- See https://en.wikipedia.org/wiki/ANSI_escape_code for more details.
888
+ r"""Color a string based on the provided color and style arguments using ANSI escape codes.
921
889
 
922
890
  This function can be called in two ways:
923
891
  - colorstr('color', 'style', 'your string')
@@ -926,14 +894,8 @@ def colorstr(*input):
926
894
  In the second form, 'blue' and 'bold' will be applied by default.
927
895
 
928
896
  Args:
929
- *input (str | Path): A sequence of strings where the first n-1 strings are color and style arguments,
930
- and the last string is the one to be colored.
931
-
932
- Supported Colors and Styles:
933
- Basic Colors: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
934
- Bright Colors: 'bright_black', 'bright_red', 'bright_green', 'bright_yellow',
935
- 'bright_blue', 'bright_magenta', 'bright_cyan', 'bright_white'
936
- Misc: 'end', 'bold', 'underline'
897
+ *input (str | Path): A sequence of strings where the first n-1 strings are color and style arguments, and the
898
+ last string is the one to be colored.
937
899
 
938
900
  Returns:
939
901
  (str): The input string wrapped with ANSI escape codes for the specified color and style.
@@ -941,6 +903,16 @@ def colorstr(*input):
941
903
  Examples:
942
904
  >>> colorstr("blue", "bold", "hello world")
943
905
  >>> "\033[34m\033[1mhello world\033[0m"
906
+
907
+ Notes:
908
+ Supported Colors and Styles:
909
+ - Basic Colors: 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
910
+ - Bright Colors: 'bright_black', 'bright_red', 'bright_green', 'bright_yellow',
911
+ 'bright_blue', 'bright_magenta', 'bright_cyan', 'bright_white'
912
+ - Misc: 'end', 'bold', 'underline'
913
+
914
+ References:
915
+ https://en.wikipedia.org/wiki/ANSI_escape_code
944
916
  """
945
917
  *args, string = input if len(input) > 1 else ("blue", "bold", input[0]) # color arguments, string
946
918
  colors = {
@@ -968,8 +940,7 @@ def colorstr(*input):
968
940
 
969
941
 
970
942
  def remove_colorstr(input_string):
971
- """
972
- Removes ANSI escape codes from a string, effectively un-coloring it.
943
+ """Remove ANSI escape codes from a string, effectively un-coloring it.
973
944
 
974
945
  Args:
975
946
  input_string (str): The string to remove color and style from.
@@ -986,8 +957,14 @@ def remove_colorstr(input_string):
986
957
 
987
958
 
988
959
  class TryExcept(contextlib.ContextDecorator):
989
- """
990
- Ultralytics TryExcept class. Use as @TryExcept() decorator or 'with TryExcept():' context manager.
960
+ """Ultralytics TryExcept class for handling exceptions gracefully.
961
+
962
+ This class can be used as a decorator or context manager to catch exceptions and optionally print warning messages.
963
+ It allows code to continue execution even when exceptions occur, which is useful for non-critical operations.
964
+
965
+ Attributes:
966
+ msg (str): Optional message to display when an exception occurs.
967
+ verbose (bool): Whether to print the exception message.
991
968
 
992
969
  Examples:
993
970
  As a decorator:
@@ -1008,22 +985,26 @@ class TryExcept(contextlib.ContextDecorator):
1008
985
  self.verbose = verbose
1009
986
 
1010
987
  def __enter__(self):
1011
- """Executes when entering TryExcept context, initializes instance."""
988
+ """Execute when entering TryExcept context, initialize instance."""
1012
989
  pass
1013
990
 
1014
991
  def __exit__(self, exc_type, value, traceback):
1015
- """Defines behavior when exiting a 'with' block, prints error message if necessary."""
992
+ """Define behavior when exiting a 'with' block, print error message if necessary."""
1016
993
  if self.verbose and value:
1017
994
  LOGGER.warning(f"{self.msg}{': ' if self.msg else ''}{value}")
1018
995
  return True
1019
996
 
1020
997
 
1021
998
  class Retry(contextlib.ContextDecorator):
1022
- """
1023
- Retry class for function execution with exponential backoff.
999
+ """Retry class for function execution with exponential backoff.
1024
1000
 
1025
- Can be used as a decorator to retry a function on exceptions, up to a specified number of times with an
1026
- exponentially increasing delay between retries.
1001
+ This decorator can be used to retry a function on exceptions, up to a specified number of times with an
1002
+ exponentially increasing delay between retries. It's useful for handling transient failures in network operations or
1003
+ other unreliable processes.
1004
+
1005
+ Attributes:
1006
+ times (int): Maximum number of retry attempts.
1007
+ delay (int): Initial delay between retries in seconds.
1027
1008
 
1028
1009
  Examples:
1029
1010
  Example usage as a decorator:
@@ -1043,7 +1024,7 @@ class Retry(contextlib.ContextDecorator):
1043
1024
  """Decorator implementation for Retry with exponential backoff."""
1044
1025
 
1045
1026
  def wrapped_func(*args, **kwargs):
1046
- """Applies retries to the decorated function or method."""
1027
+ """Apply retries to the decorated function or method."""
1047
1028
  self._attempts = 0
1048
1029
  while self._attempts < self.times:
1049
1030
  try:
@@ -1059,12 +1040,11 @@ class Retry(contextlib.ContextDecorator):
1059
1040
 
1060
1041
 
1061
1042
  def threaded(func):
1062
- """
1063
- Multi-threads a target function by default and returns the thread or function result.
1043
+ """Multi-thread a target function by default and return the thread or function result.
1064
1044
 
1065
- This decorator provides flexible execution of the target function, either in a separate thread or synchronously.
1066
- By default, the function runs in a thread, but this can be controlled via the 'threaded=False' keyword argument
1067
- which is removed from kwargs before calling the function.
1045
+ This decorator provides flexible execution of the target function, either in a separate thread or synchronously. By
1046
+ default, the function runs in a thread, but this can be controlled via the 'threaded=False' keyword argument which
1047
+ is removed from kwargs before calling the function.
1068
1048
 
1069
1049
  Args:
1070
1050
  func (callable): The function to be potentially executed in a separate thread.
@@ -1082,7 +1062,7 @@ def threaded(func):
1082
1062
  """
1083
1063
 
1084
1064
  def wrapper(*args, **kwargs):
1085
- """Multi-threads a given function based on 'threaded' kwarg and returns the thread or function result."""
1065
+ """Multi-thread a given function based on 'threaded' kwarg and return the thread or function result."""
1086
1066
  if kwargs.pop("threaded", True): # run in thread
1087
1067
  thread = threading.Thread(target=func, args=args, kwargs=kwargs, daemon=True)
1088
1068
  thread.start()
@@ -1094,8 +1074,7 @@ def threaded(func):
1094
1074
 
1095
1075
 
1096
1076
  def set_sentry():
1097
- """
1098
- Initialize the Sentry SDK for error tracking and reporting.
1077
+ """Initialize the Sentry SDK for error tracking and reporting.
1099
1078
 
1100
1079
  Only used if sentry_sdk package is installed and sync=True in settings. Run 'yolo settings' to see and update
1101
1080
  settings.
@@ -1117,25 +1096,24 @@ def set_sentry():
1117
1096
  or TESTS_RUNNING
1118
1097
  or not ONLINE
1119
1098
  or not IS_PIP_PACKAGE
1120
- or IS_GIT_DIR
1099
+ or GIT.is_repo
1121
1100
  ):
1122
1101
  return
1123
1102
  # If sentry_sdk package is not installed then return and do not use Sentry
1124
1103
  try:
1125
- import sentry_sdk # noqa
1104
+ import sentry_sdk
1126
1105
  except ImportError:
1127
1106
  return
1128
1107
 
1129
1108
  def before_send(event, hint):
1130
- """
1131
- Modify the event before sending it to Sentry based on specific exception types and messages.
1109
+ """Modify the event before sending it to Sentry based on specific exception types and messages.
1132
1110
 
1133
1111
  Args:
1134
1112
  event (dict): The event dictionary containing information about the error.
1135
1113
  hint (dict): A dictionary containing additional information about the error.
1136
1114
 
1137
1115
  Returns:
1138
- dict: The modified event or None if the event should not be sent to Sentry.
1116
+ (dict | None): The modified event or None if the event should not be sent to Sentry.
1139
1117
  """
1140
1118
  if "exc_info" in hint:
1141
1119
  exc_type, exc_value, _ = hint["exc_info"]
@@ -1145,7 +1123,7 @@ def set_sentry():
1145
1123
  event["tags"] = {
1146
1124
  "sys_argv": ARGV[0],
1147
1125
  "sys_argv_name": Path(ARGV[0]).name,
1148
- "install": "git" if IS_GIT_DIR else "pip" if IS_PIP_PACKAGE else "other",
1126
+ "install": "git" if GIT.is_repo else "pip" if IS_PIP_PACKAGE else "other",
1149
1127
  "os": ENVIRONMENT,
1150
1128
  }
1151
1129
  return event
@@ -1164,23 +1142,22 @@ def set_sentry():
1164
1142
 
1165
1143
 
1166
1144
  class JSONDict(dict):
1167
- """
1168
- A dictionary-like class that provides JSON persistence for its contents.
1145
+ """A dictionary-like class that provides JSON persistence for its contents.
1169
1146
 
1170
1147
  This class extends the built-in dictionary to automatically save its contents to a JSON file whenever they are
1171
- modified. It ensures thread-safe operations using a lock.
1148
+ modified. It ensures thread-safe operations using a lock and handles JSON serialization of Path objects.
1172
1149
 
1173
1150
  Attributes:
1174
1151
  file_path (Path): The path to the JSON file used for persistence.
1175
1152
  lock (threading.Lock): A lock object to ensure thread-safe operations.
1176
1153
 
1177
1154
  Methods:
1178
- _load: Loads the data from the JSON file into the dictionary.
1179
- _save: Saves the current state of the dictionary to the JSON file.
1180
- __setitem__: Stores a key-value pair and persists it to disk.
1181
- __delitem__: Removes an item and updates the persistent storage.
1182
- update: Updates the dictionary and persists changes.
1183
- clear: Clears all entries and updates the persistent storage.
1155
+ _load: Load the data from the JSON file into the dictionary.
1156
+ _save: Save the current state of the dictionary to the JSON file.
1157
+ __setitem__: Store a key-value pair and persist it to disk.
1158
+ __delitem__: Remove an item and update the persistent storage.
1159
+ update: Update the dictionary and persist changes.
1160
+ clear: Clear all entries and update the persistent storage.
1184
1161
 
1185
1162
  Examples:
1186
1163
  >>> json_dict = JSONDict("data.json")
@@ -1192,7 +1169,7 @@ class JSONDict(dict):
1192
1169
  >>> json_dict.clear()
1193
1170
  """
1194
1171
 
1195
- def __init__(self, file_path: Union[str, Path] = "data.json"):
1172
+ def __init__(self, file_path: str | Path = "data.json"):
1196
1173
  """Initialize a JSONDict object with a specified file path for JSON persistence."""
1197
1174
  super().__init__()
1198
1175
  self.file_path = Path(file_path)
@@ -1257,11 +1234,11 @@ class JSONDict(dict):
1257
1234
 
1258
1235
 
1259
1236
  class SettingsManager(JSONDict):
1260
- """
1261
- SettingsManager class for managing and persisting Ultralytics settings.
1237
+ """SettingsManager class for managing and persisting Ultralytics settings.
1262
1238
 
1263
1239
  This class extends JSONDict to provide JSON persistence for settings, ensuring thread-safe operations and default
1264
- values. It validates settings on initialization and provides methods to update or reset settings.
1240
+ values. It validates settings on initialization and provides methods to update or reset settings. The settings
1241
+ include directories for datasets, weights, and runs, as well as various integration flags.
1265
1242
 
1266
1243
  Attributes:
1267
1244
  file (Path): The path to the JSON file used for persistence.
@@ -1270,9 +1247,9 @@ class SettingsManager(JSONDict):
1270
1247
  help_msg (str): A help message for users on how to view and update settings.
1271
1248
 
1272
1249
  Methods:
1273
- _validate_settings: Validates the current settings and resets if necessary.
1274
- update: Updates settings, validating keys and types.
1275
- reset: Resets the settings to default and saves them.
1250
+ _validate_settings: Validate the current settings and reset if necessary.
1251
+ update: Update settings, validating keys and types.
1252
+ reset: Reset the settings to default and save them.
1276
1253
 
1277
1254
  Examples:
1278
1255
  Initialize and update settings:
@@ -1283,14 +1260,14 @@ class SettingsManager(JSONDict):
1283
1260
  """
1284
1261
 
1285
1262
  def __init__(self, file=SETTINGS_FILE, version="0.0.6"):
1286
- """Initializes the SettingsManager with default settings and loads user settings."""
1263
+ """Initialize the SettingsManager with default settings and load user settings."""
1287
1264
  import hashlib
1288
1265
  import uuid
1289
1266
 
1290
1267
  from ultralytics.utils.torch_utils import torch_distributed_zero_first
1291
1268
 
1292
- root = GIT_DIR or Path()
1293
- datasets_root = (root.parent if GIT_DIR and is_dir_writeable(root.parent) else root).resolve()
1269
+ root = GIT.root or Path()
1270
+ datasets_root = (root.parent if GIT.root and is_dir_writeable(root.parent) else root).resolve()
1294
1271
 
1295
1272
  self.file = Path(file)
1296
1273
  self.version = version
@@ -1352,11 +1329,11 @@ class SettingsManager(JSONDict):
1352
1329
  )
1353
1330
 
1354
1331
  def __setitem__(self, key, value):
1355
- """Updates one key: value pair."""
1332
+ """Update one key: value pair."""
1356
1333
  self.update({key: value})
1357
1334
 
1358
1335
  def update(self, *args, **kwargs):
1359
- """Updates settings, validating keys and types."""
1336
+ """Update settings, validating keys and types."""
1360
1337
  for arg in args:
1361
1338
  if isinstance(arg, dict):
1362
1339
  kwargs.update(arg)
@@ -1371,14 +1348,14 @@ class SettingsManager(JSONDict):
1371
1348
  super().update(*args, **kwargs)
1372
1349
 
1373
1350
  def reset(self):
1374
- """Resets the settings to default and saves them."""
1351
+ """Reset the settings to default and save them."""
1375
1352
  self.clear()
1376
1353
  self.update(self.defaults)
1377
1354
 
1378
1355
 
1379
1356
  def deprecation_warn(arg, new_arg=None):
1380
1357
  """Issue a deprecation warning when a deprecated argument is used, suggesting an updated argument."""
1381
- msg = f"'{arg}' is deprecated and will be removed in in the future."
1358
+ msg = f"'{arg}' is deprecated and will be removed in the future."
1382
1359
  if new_arg is not None:
1383
1360
  msg += f" Use '{new_arg}' instead."
1384
1361
  LOGGER.warning(msg)
@@ -1387,7 +1364,7 @@ def deprecation_warn(arg, new_arg=None):
1387
1364
  def clean_url(url):
1388
1365
  """Strip auth from URL, i.e. https://url.com/file.txt?auth -> https://url.com/file.txt."""
1389
1366
  url = Path(url).as_posix().replace(":/", "://") # Pathlib turns :// -> :/, as_posix() for Windows
1390
- return unquote(url).split("?")[0] # '%2F' to '/', split https://url.com/file.txt?auth
1367
+ return unquote(url).split("?", 1)[0] # '%2F' to '/', split https://url.com/file.txt?auth
1391
1368
 
1392
1369
 
1393
1370
  def url2file(url):
@@ -1428,7 +1405,6 @@ TESTS_RUNNING = is_pytest_running() or is_github_action_running()
1428
1405
  set_sentry()
1429
1406
 
1430
1407
  # Apply monkey patches
1431
- torch.load = torch_load
1432
1408
  torch.save = torch_save
1433
1409
  if WINDOWS:
1434
1410
  # Apply cv2 patches for non-ASCII and non-UTF characters in image paths